require(plotly)
require(tidyverse)
require(ggridges)
require(cowplot)
require(RColorBrewer)
require(grid)
require(ggtext)
old <- theme_set(theme_bw(base_size = 16))

This is the second part of the analysis. In the first part (see 20231229-pool-qc-PHO5.Rmd), I did QC and exported the filtered dataset. Here, I will continue working with that dataset and answer our biological questions. This notebook is an updated copy of 20240103-PHO5-analysis.Rmd. The main update is to compare the new 4/5 break point chimeras to the old ones.

Goal

  • Analyze the full chimera set flow results for PHO5pr-mCherry reporter.
  • Develop an analysis pipeline to perform QC, correction (if needed) and plotting the results.
  • Compare the new 4/5 breakpoint chimeras to the old set.
Symbol Old New Comment
CCCCS 210 406
CCCSC 211 422
SCCCS 216 408 =332
CCSSC 218 420
CSCSC 223 423
SCSCS 224 414
SSSCS 229 415
SSSSC 233 407
SSCCS 235 409
SSCSC 239 424
CCSCS 240 413
CSSSC 241 410
CSSCS 250 412
SCCSC 251 425
SCSSC 252 421
CSCCS 253 411
CSScsC 254 419
CCCcsC 258 418
SSSscS 278 416
CSSscS 279 417

Data

Import the background subtracted data

dat0 <- read_tsv("../input/20231230-PHO5-bg-subtracted-data.tsv", col_types = "cccccdddddc")
print("Date of experiments and # of failed samples")
[1] "Date of experiments and # of failed samples"
with(dat0, table(date, flag))
       flag
date    fail pass
  02/08    0   88
  02/09    0   89
  02/10   85    0
  02/11    2   84
  02/16    3   86
  02/18    2   87
  02/19    2   87
  02/20    0   90
  02/21    0   90
  02/22    1   85
  02/23    1   87
  03/30    0   54
  03/31    0   54
  12/28    0  178
  12/29    1  178

Filter the data

dat <- filter(dat0, host != "PHO84", flag == "pass", date != "02/10") %>% 
  # based on previous QC, the following sample (both replicates) have high
  # variance - one biological replicate is highly expressed, while the other 
  # two have mNeon, but barely any RFP expression.
  mutate(flag = case_when(
    plasmid == "233" & host == "pho2∆" ~ "high.var", 
    plasmid == "423" & host == "PHO2" ~ "high.var", 
    .default = flag
  ))

Chimera makeup information

meta <- read_tsv("../input/20231228-chimera-Pho4-makeup.txt", 
                 comment = "#", col_types = "ccccc")

Summarize data

Here we would like calculate the ratio of RFP/GFP for each chimera (plasmid) across all replicates, including from different days. Note that the parameter of interest is a ratio, which can be estimated using either “means of ratios” or “ratios of means”. These are just two specific instances of a more general estimator, representing two choices of the weights. The “means of ratios” first calculates the ratios for each replicate within a plasmid, then average them. In this calculation, each replicate is given the weight of 1/n (equal). The “ratios of means” first sum up the GFP and RFP values separately across the replicates for each plasmid, then take the ratio between them. In this estimator, the weight for each replicate is x / sum(x), where x is the denominator in the ratio, i.e., GFP. In other words, this estimator will give more weights to the replicates where the chimera had a higher expression level.

Both estimators are known to be biased. We will ignore that for the moment. In terms of a choice between the two, it seems that there is no reason to give more weights to the experiments with a higher GFP signal. So, the “means of ratios” seems a more natural choice. However, we will calcultae both and dedice later.

A final question is how to calculate the variance of the ratio estimate. According to the survey package manual, an approximate estimator for the variance is

\[ r = \frac{\bar{y}}{\bar{x}}, \text{where}\ \bar{y}=\frac{1}{n}\sum_{i=1}^{n}y_i\ \text{and}\ \bar{x}=\frac{1}{n}\sum_{i=1}^{n}x_i\ \\ \hat{V}(r) = (1-\frac{n}{N})(\frac{1}{\bar{x}^2})\frac{s_r^2}{n}\ \text{where}\ s_r^2=\frac{1}{n-1}\sum_{i=1}^{n}(y_i-rx_i)^2 \]

Assuming that N>>n, we can ignore the first term in the variance estimator. The rest can be calculated from the data

datsum <- dat %>%
  filter(!is.na(plasmid)) %>% 
  group_by(plasmid, host) %>% 
  summarize(
     n = n(),
    mG = mean(BL1.H),
    mR = mean(YL2.H),
     A = mean(YL2.H/BL1.H),
     r = mR/mG,
    s2 = 1/(n-1)*sum((YL2.H - r*BL1.H)^2),
    vr = 1/(mG^2)*s2/n,
    se = sqrt(vr),
    .groups = "drop"
  ) %>% 
  select(-s2, -vr)# %>% 
  #pivot_wider(names_from = host, values_from = BL1.H:`nR/G`) %>% 
  #mutate(`pho2∆/PHO2` = `R/G_pho2∆`/`R/G_PHO2`,
  #       `n.pho2∆/PHO2` = `nR/G_pho2∆`/`nR/G_PHO2`)

For each chimera, we would also like to calculate three values:

  1. A in pho2∆: this is its base activity without Pho2
  2. A in PHO2: this is its full activity with Pho2
  3. A_PHO2 / A_pho2∆: this is the Pho2 enhancement of activity

We assign the chimeras into several groups, based on their A_PHO2 and A_PHO2/A_pho2∆

ximera <- datsum %>%
  mutate(host = fct_recode(host, "pho2" = "pho2∆")) %>% 
  pivot_wider(id_cols = plasmid, names_from = host, values_from = A, names_prefix = "A_") %>% 
  mutate(
    s_PHO2 = A_PHO2 / A_PHO2[plasmid == "194"],
    s_pho2 = A_pho2 / A_pho2[plasmid == "194"],
    boost = A_PHO2 / A_pho2,
    group = case_when(
      plasmid %in% c("188", "194") ~ "ref",
      s_PHO2 < 0.2                 ~ "n.f.",
      .default = "chimera"
    ),
    group = fct_relevel(group, "ref", "chimera", "n.f.")
  ) %>% 
  right_join(select(meta, plasmid, set, symbol, full), by = "plasmid") %>% 
  mutate(symbol = fct_reorder(symbol, s_PHO2, .desc = TRUE)) %>% 
  relocate(c(set, symbol, group), .after = plasmid)

Did the new break points rescue?

ximera %>% 
  filter(set %in% c("N", "A", "SA", "SN")) %>% 
  arrange(symbol) %>% 
  mutate(across(where(is.numeric), ~ round(.x, 2)))# %>% 
  #write_tsv("../output/20240103-compare-old-new-4-5-breakpoints.tsv")

Export the summarized data

write_tsv(ximera, file = "../output/20240103-PHO5pr-chimera-summarized.tsv")

For this analysis, include the set information in symbol

Data selection

Write a function that can select a subset of the chimeras given a set of rules

my_data_select <- function(pattern = NULL, Set = NULL){
  # change region 4 into a consistent format
  tmp <- ximera %>% 
    mutate(
      symbol = as.character(symbol),
      Symbol = ifelse(
        nchar(symbol) == 5,
        paste0(str_sub(symbol, 1, 3), 
               str_sub(symbol, 4, 4), 
               str_sub(symbol, 4, 4), 
               str_sub(symbol, 5, 5)),
        symbol
      )) %>% 
    select(plasmid, Symbol)
  # starting set
  if(length(Set) == 0)
    xim <- filter(tmp, !plasmid %in% refs)
  else
    xim <- filter(tmp, set %in% Set, !plasmid %in% refs)
  # compare to the pattern
  try(if(nchar(pattern) != 6) stop("Pattern must be a string with 6 characters"))
  symbols = xim$Symbol # extrac the symbols for testing
  include = nchar(symbols) > 0 # initialize the inclusion vector
  for(i in 1:6){
    p = substr(pattern, i, i)
    if(p != "X" & p != "x"){ # ignore X and x
      test = toupper(str_sub(symbols, i, i)) == toupper(p)
      include = include & test
    }
  }
  select <- cbind(xim, include)
  return(select$plasmid[select$include])
}
my_data_select_m <- function(patterns = NULL, Set = NULL){
  all_selected = c()
  try(if(length(patterns) == 0) stop("No patterns provided"))
  for(i in patterns)
    all_selected = c(all_selected, my_data_select(pattern = i))
  return(unique(all_selected))
}

Plotting functions

Set up common parameters for thresholding and plotting

# reference Pho4 plasmid ids
refs <- c("188", "194")
# colors
date.colors = c(brewer.pal(name="Dark2", n = 8), brewer.pal(name="Paired", n = 8))
host.colors = c("PHO2" = "gray30", "pho2∆" = "gray70")
point.colors = c("PHO2" = "forestgreen", "pho2∆" = "purple4")
# 

Data prep and transform

my_data_prep <- function(selection){
  # given a selection of chimera ID (plasmid), prepare a data frame for plotting
  # subset data
  tmp <- ximera1 %>% 
    filter(plasmid %in% c(refs, selection)) %>% 
    select(plasmid, symbol, group) %>% 
    inner_join(dat, by = "plasmid") %>% 
  return(tmp)
}

Plot RFP/GFP ratio and individual components

my_plot_ratio <- function(selection){
  tmp <- my_data_prep(selection)
  p <- tmp %>% 
    select(-c(FSC.H, nGFP, nRFP, flag)) %>% 
    mutate(`R/G` = YL2.H/BL1.H) %>% 
    pivot_longer(cols = c(BL1.H, YL2.H, `R/G`), 
                 names_to = "parameter", values_to = "value") %>% 
    mutate(parameter = factor(parameter, levels = c("R/G", "YL2.H", "BL1.H"),
                              labels = c("RFP/GFP", "PHO5pRFP", "Pho4-GFP"))) %>% 
    ggplot(aes(x = symbol, y = value, group = host)) + 
    stat_summary(aes(group = host), fun.data = "mean_cl_boot", geom = "errorbar",
                 position = position_dodge(0.5), width = 0.3) +
    geom_bar(aes(fill = host), width = 0.5, alpha = 0.8,
             stat = "summary", fun = "mean", position = position_dodge(0.5)) +
    geom_point(data = function(x) subset(x, !symbol %in% c("CCCCC", "SSSSS")),
               aes(group = host, color = host), size = 1, shape = 3, alpha = 0.9,
               position = position_jitterdodge(dodge.width = 0.5, jitter.width = 0.1)) +
    scale_color_manual(values = point.colors) +
    scale_fill_manual(values = host.colors) +
    facet_grid(parameter~group, scales = "free", space = "free_x") +
    theme_bw(base_size = 18) + background_grid(minor = "none") + 
    xlab("Pho4 chimera") +
    theme(axis.text.x = element_text(angle = 30, hjust = 1, family = "mono"),
          legend.position = "top",
          axis.title = element_blank())
  return(p) 
}

Plot chimera relative activity to ScPho4 w/ Pho2 and boost factor

my_plot_rel_act <- function(selection){
  # given a selection of chimera IDs, plot their functionality w/PHO2
  # relative to ScPho4, and their boost
  p <- filter(ximera, plasmid %in% c(refs, selection)) %>%
    ggplot(aes(x = symbol, y = s_PHO2)) +
    geom_col(width = 0.3, color = "black", fill = "gray80") + 
    geom_hline(yintercept = 1, linetype = 2, color = "gray30") +
    facet_grid(.~group, scales = "free_x", space = "free_x") + 
    scale_y_continuous(labels = scales::percent) +
    xlab("Pho4 chimera") + ylab("A<sub>PHO2</sub>, chimera/ScPho4") +
    ggtitle("Chimera function in <em>S. cerevisiae</em>") +
    theme_bw(base_size = 18) + background_grid(minor = "none") +
    theme(axis.text.x = element_text(angle = 30, hjust = 1, family = "mono"),
          #strip.text = element_blank(), 
          plot.title = element_markdown(hjust = 0.5),
          axis.title.y = element_markdown(),
          legend.position = "top")
  return(p)
}
my_plot_boost <- function(selection){
  # given a selection of chimera IDs, plot their functionality w/PHO2
  # relative to ScPho4, and their boost
  # dat
  tmp <- filter(ximera, plasmid %in% c(refs, selection)) %>% 
    mutate(perc_pho2 = A_pho2/A_PHO2) %>% 
    pivot_longer(cols = c(s_PHO2, boost, perc_pho2), 
                 names_to = "parameter", values_to = "ratio")
  # labeller
  par.explain <- c(
    s_PHO2 = "Rel. A<sub>PHO2</sub>",
    boost = "Boost",
    perc_pho2 = "%A<sub>pho2∆</sub>"
  )
  p <- ggplot(tmp, aes(x = symbol, y = ratio)) +
    geom_col(width = 0.3, color = "black", fill = "gray80") +
    geom_hline(yintercept = 1, linetype = 2, color = "gray30") +
    facet_grid(parameter~group, scales = "free", space = "free_x",
              labeller = labeller(parameter = par.explain)) +
    theme_bw(base_size = 18) +
    background_grid(minor = "none") +
    theme(axis.text.x = element_text(angle = 30, hjust = 1, family = "mono"),
          axis.title.x = element_blank(),
          strip.text.y = element_markdown(size = rel(0.9))
          #plot.title = element_text(hjust = 0.5)
    )
  return(p)
}

High variance samples

Summarize the background subtracted data by calculating the means and cv for each strain.

cv <- dat %>% 
  select(-nGFP, -nRFP) %>%
  pivot_longer(FSC.H:YL2.H, names_to = "parameter", values_to = "intensity") %>% 
  group_by(date, plasmid, host, parameter) %>% 
  summarize(
    n = n(),
    mean = mean(intensity),
    cv = sd(intensity)/mean(intensity),
    .groups = "drop"
  ) %>% 
  arrange(desc(cv))

Number of replicates left for each sample

expt <- dat %>% 
  filter(host %in% c("PHO2", "pho2∆"), !plasmid %in% c("188", "194")) %>% 
  group_by(date, plasmid, host) %>% 
  summarize(n = n(), .groups = "drop")

expt %>% 
  ggplot(aes(x = plasmid, y = n)) +
  geom_col(aes(fill = host)) + 
  facet_grid(date ~ .) +
  scale_fill_manual(values = c("PHO2" = "gray30", "pho2∆" = "gray70")) +
  theme_minimal() + background_grid(major = "none") + panel_border(size = 0.5) +
  scale_y_continuous(name = "Replicates", breaks = c(6)) + xlab(NULL) +
  theme(axis.text.x = element_text(angle = 90),
        strip.text.y = element_text(angle = 0),
        legend.position = "top")

Use the control strain (pH194 with PHO2) to identify and correct for systematic biases

control <- filter(dat, plasmid == "194", host == "PHO2") %>% 
  separate(well, into = c("row", "col"), sep = 1) %>% 
  droplevels()

Model for mNeon

gfp.model.0 <- lm(BL1.H ~ log10(events) + date + row*col, data = control)
step(gfp.model.0)
Start:  AIC=2047.53
BL1.H ~ log10(events) + date + row * col

                Df Sum of Sq      RSS    AIC
- row:col        6    117125  6384575 2039.1
- log10(events)  1       159  6267609 2045.5
<none>                        6267450 2047.5
- date          13   4256724 10524173 2121.0

Step:  AIC=2039.08
BL1.H ~ log10(events) + date + row + col

                Df Sum of Sq      RSS    AIC
- log10(events)  1        15  6384590 2037.1
- row            3    195156  6579731 2038.9
<none>                        6384575 2039.1
- col            2    625788  7010363 2053.0
- date          13   4242245 10626820 2110.9

Step:  AIC=2037.09
BL1.H ~ date + row + col

       Df Sum of Sq      RSS    AIC
- row   3    197679  6582269 2036.9
<none>               6384590 2037.1
- col   2    630432  7015023 2051.2
- date 13   5905463 12290054 2136.8

Step:  AIC=2036.94
BL1.H ~ date + col

       Df Sum of Sq      RSS    AIC
<none>               6582269 2036.9
- col   2    630432  7212702 2050.5
- date 13   5905463 12487733 2133.9

Call:
lm(formula = BL1.H ~ date + col, data = control)

Coefficients:
(Intercept)    date02/09    date02/11    date02/16    date02/18    date02/19    date02/20  
    1792.20      -209.67      -259.92      -351.46      -242.33      -319.04      -499.04  
  date02/21    date02/22    date02/23    date03/30    date03/31    date12/28    date12/29  
    -343.79      -233.12      -439.50      -533.58      -569.08      -634.44      -548.06  
       col5         col9  
     -72.77      -140.33  
gfp.model.1 <- lm(BL1.H ~ date + col, data = control)

Model for PHO5pr::RFP

rfp.model.0 <- lm(YL2.H ~ log10(events) + date + row*col, data = control)
step(rfp.model.0)
Start:  AIC=2986.52
YL2.H ~ log10(events) + date + row * col

                Df  Sum of Sq        RSS    AIC
- row:col        6   15521638  849284378 2978.1
- log10(events)  1    6696987  840459727 2986.1
<none>                         833762740 2986.5
- date          13 1538501384 2372264124 3161.3

Step:  AIC=2978.06
YL2.H ~ log10(events) + date + row + col

                Df  Sum of Sq        RSS    AIC
- log10(events)  1    5909230  855193608 2977.4
<none>                         849284378 2978.1
- col            2   39191418  888475796 2982.7
- row            3  182437263 1031721641 3009.4
- date          13 1539530911 2388815289 3150.6

Step:  AIC=2977.39
YL2.H ~ date + row + col

       Df  Sum of Sq        RSS    AIC
<none>                855193608 2977.4
- col   2   42651346  897844954 2982.7
- row   3  192060792 1047254400 3010.3
- date 13 2411146165 3266339773 3208.7

Call:
lm(formula = YL2.H ~ date + row + col, data = control)

Coefficients:
(Intercept)    date02/09    date02/11    date02/16    date02/18    date02/19    date02/20  
    31443.4      -6198.7      -4381.6      -5892.7      -2929.5      -6150.9      -4224.1  
  date02/21    date02/22    date02/23    date03/30    date03/31    date12/28    date12/29  
    -4476.0      -3672.5      -4728.8      -8207.8      -9870.6     -11549.9     -11786.6  
       rowC         rowE         rowG         col5         col9  
    -1849.2      -2119.1      -2659.6       -784.3      -1125.8  
rfp.model.1 <- lm(YL2.H ~ log10(events) + date + row + col, data = control)

there are more systematic shifts in the RFP, significant for row, col, date and also # of events however, I won’t be removing these effects yet, because I’ve found that RFP/GFP ratios are pretty consistent across days. In other words, the variation in GFP and RFP may be cancelled out.

Check for each plasmid how consistent are the measurements between days

tmp <- dat %>% 
  # remove one sample with only one valid day of experiment
  filter(!(plasmid == "218" & host == "PHO2"), !plasmid %in% c("188", "194", NA)) %>% 
  nest(data = c(date, BL1.H, YL2.H), .by = c(plasmid, host))

day.var.gfp <- tmp %>% 
  mutate(model = map(data, function(df) lm(BL1.H ~ date, data = df)),
         tidied = map(model, broom::tidy)) %>% 
  unnest(tidied) %>% 
  filter(term != "(Intercept)") %>% 
  mutate(p.adj = p.adjust(p.value, method = "BH")) %>% 
  select(-data, -model) %>% 
  filter(p.adj < 0.10) %>% 
  arrange(plasmid, host)

day.var.rfp <- tmp %>% 
  mutate(model = map(data, function(df) lm(YL2.H ~ date, data = df)),
         tidied = map(model, broom::tidy)) %>% 
  unnest(tidied) %>% 
  filter(term != "(Intercept)") %>% 
  mutate(p.adj = p.adjust(p.value, method = "BH")) %>% 
  select(-data, -model) %>% 
  filter(p.adj < 0.10) %>% 
  arrange(plasmid, host)
# extract ximera names
refs <- c("188","194")
# make a test set
day.var.gfp.list <- unique(day.var.gfp$plasmid)
day.var.rfp.list <- unique(day.var.rfp$plasmid)

High day-to-day GFP variance: 212, 222, 229, 251, 277, 301, 326, 328, 334 High day-to-day RFP variance: 212, 216, 239, 241

Plotting components for chimeras with high day-to-day variance in Pho4-mNeon

p <- my_plot_ratio(c(refs,day.var.gfp.list)) + 
    geom_point(data = function(x) subset(x, !symbol %in% c("CCCCC", "SSSSS")),
               aes(group = host, color = date), size = 1, shape = 3, alpha = 0.9,
               position = position_jitterdodge(dodge.width = 0.5, jitter.width = 0.1)) +
    scale_color_manual(values = date.colors, guide = "none")
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
p

Watch out for CSCscC, SCCsS, SCCsS

Plotting components for chimeras with high day-to-day variance in PHO5pr-mCherry

p <- my_plot_ratio(c(refs,day.var.rfp.list)) + 
    geom_point(data = function(x) subset(x, !symbol %in% c("CCCCC", "SSSSS")),
               aes(group = host, color = date), size = 1, shape = 3, alpha = 0.9,
               position = position_dodge(width = 0.5)) +
    scale_color_manual(values = date.colors, guide = "none")
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
p

most of the day-to-day variance are canceled out after RFP/GFP normalization

All chimera, scatter plot

Design the plot

my_scatter_plot <- function(pattern){
  selection = my_data_select(pattern = pattern)
  scatter.colors = c("ScPho4" = "forestgreen", "CgPho4" = "blue3", "cyan2", "other" = "gray20")
  names(scatter.colors)[3] = pattern
  p <- ximera %>% 
    mutate(A_PHO2 = signif(A_PHO2, digits = 2),
           A_pho2 = signif(A_pho2, digits = 2),
           group = case_when(
             symbol == "CCCCC" ~ "CgPho4",
             symbol == "SSSSS" ~ "ScPho4",
             plasmid %in% selection ~ pattern,
             .default = "other"
           ),
           group = fct_relevel(group, names(scatter.colors))) %>% 
    ggplot(aes(x = A_PHO2, y = A_pho2, label = symbol)) + 
    geom_point(aes(color = group), size = 2.5) + 
    scale_color_manual("Pho4 type", values = scatter.colors) +
    geom_abline(slope = 1) +
    theme_gray(base_size = 14)
  return(p)
}

this function is the same as above, but is used to plot region 4 effects alone, and doesn’t take any input

my_scatter_plot_fix <- function(){
  # this function is the same as above, but is used to plot region 4
  # effects alone, and doesn't take any input
  s1 = my_data_select(pattern = "XXXCCX")
  s2 = my_data_select(pattern = "XXXSSX")
  scatter.colors = c("ScPho4" = "forestgreen", "CgPho4" = "blue3", 
                     "P2ID:Cg" = "cyan2", "P2ID:Sc" = "palegreen",
                     "P2ID:mixed" = "gray20")
  p <- ximera %>% 
    mutate(A_PHO2 = signif(A_PHO2, digits = 2),
           A_pho2 = signif(A_pho2, digits = 2),
           group = case_when(
             symbol == "CCCCC" ~ "CgPho4",
             symbol == "SSSSS" ~ "ScPho4",
             plasmid %in% s1 ~ "P2ID:Cg",
             plasmid %in% s2 ~ "P2ID:Sc",
             .default = "P2ID:mixed"
           ),
           group = fct_relevel(group, names(scatter.colors))) %>% 
    ggplot(aes(x = A_PHO2, y = A_pho2, label = symbol)) + 
    geom_abline(slope = 1) +
    geom_point(aes(color = group), size = 2.5) + 
    scale_color_manual(NULL, values = scatter.colors) +
    labs(x = bquote(A[PHO2]), y = bquote(A[pho2])) +
    theme_minimal(base_size = 16) +
    theme(legend.text = element_text(size = rel(0.75)))
  return(p)
}

this function is the same as my_scatter_plot_fix except that it plots all the chimeras without coloring them differently. for figure 5

my_scatter_plot_all <- function(){
  # this function is the same as my_scatter_plot_fix except that it plots all the chimeras
  # without coloring them differently. for figure 5
  s1 = my_data_select(pattern = "XXXCCX")
  s2 = my_data_select(pattern = "XXXSSX")
  scatter.colors = c("ScPho4" = "forestgreen", "CgPho4" = "blue3", 
                     "P2ID:Cg" = "gray20", "P2ID:Sc" = "gray20",
                     "P2ID:mixed" = "gray20")
  scatter.size = c("ScPho4" = 3.5, "CgPho4" = 3.5,
                   "P2ID:Cg" = 2.5, "P2ID:Sc" = 2.5, "P2ID:mixed" = 2.5)
  p <- ximera %>% 
    mutate(A_PHO2 = signif(A_PHO2, digits = 2),
           A_pho2 = signif(A_pho2, digits = 2),
           group = case_when(
             symbol == "CCCCC" ~ "CgPho4",
             symbol == "SSSSS" ~ "ScPho4",
             plasmid %in% s1 ~ "P2ID:Cg",
             plasmid %in% s2 ~ "P2ID:Sc",
             .default = "P2ID:mixed"
           ),
           group = fct_relevel(group, names(scatter.colors))) %>% 
    ggplot(aes(x = A_PHO2, y = A_pho2, label = symbol)) + 
    geom_abline(slope = 1) +
    geom_point(aes(color = group, size = group)) + 
    scale_color_manual(NULL, values = scatter.colors) +
    scale_size_manual(values = scatter.size, guide = "none") +
    labs(x = bquote(A[PHO2]), y = bquote(A[pho2])) +
    theme_cowplot() + panel_border(color = "gray30", size = 1.2) +
    theme(legend.text = element_text(size = rel(0.8)),
          legend.position = "none",
          axis.title = element_text(face = 2, size = rel(1.2)),
          axis.line = element_blank())

  return(p)
}

ANOVA

split <- c(1,1,1,1,1); names(split) <- paste0("P", 1:5)
tmp <- ximera %>% 
  filter(set == "M", group != "n.f.") %>% 
  separate_wider_position(symbol, split) %>% 
  mutate(across(P1:P5, ~factor(.x, levels = c("S", "C"))))
lm <- lm(A_pho2 ~ (P1+P2+P3+P4+P5), data = tmp)
summary(lm)

Call:
lm(formula = A_pho2 ~ (P1 + P2 + P3 + P4 + P5), data = tmp)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.7061 -1.0599 -0.3169  0.4672  4.2623 

Coefficients: (1 not defined because of singularities)
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   -1.290      1.205  -1.071 0.312150    
P1C            2.728      1.087   2.509 0.033368 *  
P2C            1.811      1.040   1.742 0.115433    
P3C            3.602      1.087   3.314 0.009032 ** 
P4C            5.224      1.087   4.805 0.000967 ***
P5C               NA         NA      NA       NA    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.945 on 9 degrees of freedom
Multiple R-squared:  0.8555,    Adjusted R-squared:  0.7913 
F-statistic: 13.33 on 4 and 9 DF,  p-value: 0.0008028

The main effects were calculated by averaging over all chimeras with CgPho4 region at the respective position. I’d like to break them down by backgrounds. For example, for region 3, I’d like to see the pairwise comparisons between CCCSS and CCSSS, where only region 3 differs. The steps are

  1. select the region to be compared. split the symbol into two parts - the genotype of the focal region and the rest
  2. group by the second part (rest) and calculate the differential
my_calc_region_effect <- function(region, variable){
  # this function takes the name of a variable of interest
  # x specifies the foreground region, which will be examined for its effect on
  # the variable of interest.
  # it then transforms the ximera data frame to preserve only the variable of
  # interest, pivots it wider after grouping by the background composition.
  
  # prepare the data by mutating the symbol column into fg and bg
  valid.var <- c("A_PHO2", "A_pho2", "s_PHO2", "spho2", "boost")
  if(!variable %in% valid.var)
    stop(paste0("Please specify one of the valid variable names:", 
                paste(valid.var, collapse = ", ")))
  tmp <- ximera %>% 
    filter(set == "M") %>% 
    select(plasmid, symbol, var = {{ variable }}) %>% 
    mutate(fg = str_sub(symbol, region, region) %>% toupper(),
           bg = symbol %>% toupper())
  # replace the foreground region with X for grouping
  str_sub(tmp$bg, region, region) <- "X"
  # reorganize the tibble for easier handling, optional
  tmp <- relocate(tmp, fg, bg, .before = symbol) %>% select(-symbol)
  # pivot the data into a wide format such that for each background, there
  # are two values for the variable of interest, one from the chimera with 
  # CgPho4's version in the foreground and another with ScPho4's version
  tmp <- tmp %>% 
    select(plasmid, fg, bg, var) %>% 
    pivot_wider(id_cols = bg, names_from = "fg", 
                values_from = c(plasmid, var)) %>% 
    unite(plasmid, starts_with("plasmid")) %>%
    mutate(label = paste(bg, plasmid, sep = "\n"))
  return(tmp)
}

my_plot_region_effect_onevar <- function(region, variable){
  # this function uses `my_calc_region_effect` output as the data
  # and makes a xy scatter plot, where x shows the value of the variable of 
  # interest with CgPho4 in the focal region, and y for the ScPho4 version
  tmp <- my_calc_region_effect(region, variable)
  p <- ggplot(tmp, aes(x = var_C, y = var_S, label = label)) +
    geom_point(size = 2.5) + 
    geom_abline(slope = 1) +
    xlab(paste0("Region ", region, " from CgPho4")) +
    ylab(paste0("Region ", region, " from ScPho4")) +
    xlim(0, NA) + ylim(0, NA) +
    ggtitle(paste0("Effect on ", variable)) +
    theme_gray(base_size = 16) +
    theme(plot.title = element_text(hjust = 0.5))
  return(p)
}
x = 5
p1 <- my_plot_region_effect_onevar(x, "A_PHO2")
p2 <- my_plot_region_effect_onevar(x, "A_pho2")
subplot(p1, p2, margin = 0.05) %>% 
  layout(title = paste("Region", x, "swap effect on A_PHO2 and A_pho2", sep = " "),
         xaxis = list(title = paste0("Region ", x, " from CgPho4")),
         yaxis = list(title = paste0("Region ", x, " from ScPho4")) )

Here, I’d like to take what I build above and create a new tibble, in which each row is a different background (makeup of the chimera except for the focal region). The value columns are:

  1. dA_PHO2 = A_PHO2_Cg - A_PHO2_Sc
  2. dA_pho2 = A_pho2_Cg - A_pho2_Sc
  3. A_PHO2_Sc = A_PHO2_Sc

The goal is to plot dA_PHO2 and dA_pho2 side-by-side for each background.

my_comp_region_effect <- function(region){
  # this function uses my_calc_region_effect to get the value for the variable of interest
  # with either Cg or Sc version in the focal region, separately for each background composition
  # it does so for two variables, A_PHO2 and A_pho2, then calculate dA_PHO2, dA_pho2, and
  # combine them
  PHO2 = my_calc_region_effect(region, "A_PHO2") %>% 
    mutate(dA_PHO2 = var_C - var_S,
           # mean A_PHO2
           M_PHO2 = (var_S + var_C)/2,
           NF = ifelse(M_PHO2 <=3.5, TRUE, FALSE)) %>% 
    select(-var_S, -var_C)
  
  pho2 = my_calc_region_effect(region, "A_pho2") %>% 
    mutate(dA_pho2 = var_C - var_S, 
           M_pho2 = (var_S + var_C)/2) %>% 
    select(-var_S, -var_C)
  
  dat <- full_join(PHO2, pho2, by = c("bg", "plasmid", "label")) %>% 
    select(bg, plasmid, dA_PHO2, dA_pho2, M_PHO2, M_pho2, NF)
  
  return(dat)
}

my_plot_region_effect_twovar_line <- function(region, highlight = "none"){
  # this function uses my_comp_region_effect to generate the data
  # and plot the difference in A_PHO2 and A_pho2 between the CgPho4 vs ScPho4
  # in the focal region
  dat <- my_comp_region_effect(region) %>% 
    pivot_longer(cols = c(dA_PHO2, dA_pho2), 
                 names_to = "host", values_to = "diff") %>% 
    mutate(host = fct_recode(host, `PHO2` = "dA_PHO2", `pho2∆` = "dA_pho2"),
           host = fct_relevel(host, "PHO2"))
  if(highlight != "none" & highlight != region){
    hl = as.numeric(highlight)
    dat <- mutate(dat, grp = str_sub(bg, hl, hl) %>% toupper(),
                  grp = fct_recode(grp, CgPho4 = "C", ScPho4 = "S"))
  }else{
    dat <- mutate(dat, grp = ifelse(NF, "n.f.", "others"))
  }
  # specify legend title
  legend.title = ""
  if(highlight != "none" & highlight != region){
    hl = as.numeric(highlight)
    dat <- mutate(dat, grp = str_sub(bg, hl, hl) %>% toupper(),
                  grp = fct_recode(grp, CgPho4 = "C", ScPho4 = "S"))
    legend.title = paste("Region", highlight, sep = " ")
  }else{
    dat <- mutate(dat, grp = ifelse(NF, "no", "yes"))
    legend.title = "Functional"
  }
  # specify arrow annotation
  arrow.x = 0.7
  arrow.y = (max(dat$diff) - min(dat$diff)) / 5 
  p <- dat %>% 
    ggplot(aes(x = host, y = diff, label = bg)) +
    geom_point(aes(color = grp), size = 2, alpha = 0.8,
               position = position_jitter(0.05)) + 
    geom_line(aes(group = bg), linewidth = 0.2, alpha = 0.8) +
    geom_segment(aes(x = arrow.x, xend = arrow.x, y = -arrow.y, yend = arrow.y),
                 arrow = arrow(length = unit(0.03, "npc"), ends = "both"), 
                 color = "gray60", lwd = 1, alpha = 0.5) +
    geom_segment(aes(x = arrow.x - 0.05, xend = arrow.x + 0.05, y = 0, yend = 0),
                 lwd = 2, color = "gray60") +
    annotate("text", x = arrow.x - 0.1, y = 5, label = "CgPho4++", 
             angle = '90', color = "gray30") +
    annotate("text", x = arrow.x + 0.1, y = -5, label = "ScPho4++", 
             angle = '270', color = "gray30") +
    scale_color_manual(legend.title, values = c("orange", "gray20")) +
    ylab("Region swap effect (Cg-Sc)") +
    theme_bw(base_size = 18) + 
    theme(
      axis.title.x = element_blank(),
      axis.title.y = element_text(size = rel(0.9)),
      legend.text = element_text(size = rel(0.8)),
      legend.title = element_text(size = rel(0.9)),
    )
  return(p)
}

my_plot_region_effect_twovar_line("4", "5")# %>% ggplotly()
ggsave("../img/20231221-region-swap-effect-4-on-5.png", width = 6, height = 4, dpi = 150)
my_plot_region_effect_twovar_line("5", "4")# %>% ggplotly()
ggsave("../img/20231224-region-swap-effect-5-on-4.png", width = 6, height = 4, dpi = 200)
my_plot_region_effect_twovar_line_par <- function(regions, highlight = "none"){
  # this function uses my_comp_region_effect to generate the data
  # and plot the difference in A_PHO2 and A_pho2 between the CgPho4 vs ScPho4
  # in the focal region
  dat <- map_dfr(regions, \(region) my_comp_region_effect(region), .id = "region") %>% 
    pivot_longer(cols = c(dA_PHO2, dA_pho2), 
                 names_to = "host", values_to = "diff") %>% 
    mutate(host = fct_recode(host, `PHO2` = "dA_PHO2", `pho2∆` = "dA_pho2"),
           host = fct_relevel(host, "PHO2"))
  # specify legend title
  legend.title = ""
  if(highlight != "none" & !highlight %in% regions){
    hl = as.numeric(highlight)
    dat <- mutate(dat, grp = str_sub(bg, hl, hl) %>% toupper(),
                  grp = fct_recode(grp, CgPho4 = "C", ScPho4 = "S"))
    legend.title = paste("Region", highlight, "from", sep = " ")
  }else{
    dat <- mutate(dat, grp = ifelse(NF, "no", "yes"))
    legend.title = "Functional"
  }
  # specify arrow annotation
  arrow.x = 0.7
  arrow.y = (max(dat$diff) - min(dat$diff)) / 5 
  p <- dat %>% 
    ggplot(aes(x = host, y = diff, label = bg)) +
    geom_point(aes(color = grp), size = 2, alpha = 0.8,
               position = position_jitter(0.1)) + 
    geom_line(aes(group = bg), linewidth = 0.2, alpha = 0.8) +
    facet_grid(grp ~ region, labeller = labeller(
      grp = c(CgPho4 = "P2ID:CgPho4", ScPho4 = "P2ID:ScPho4"),
      region = label_both
    )) +
    scale_color_manual("P2ID:", values = c("orange", "gray20")) +
    ylab("Region swap effect (Cg-Sc)") +
    theme_bw(base_size = 18) + 
    theme(
      axis.title.x = element_blank(),
      axis.title.y = element_text(size = rel(0.9)),
      legend.text = element_text(size = rel(0.8)),
      legend.title = element_text(size = rel(0.9)),
    )
  return(p)
}
my_plot_region_effect_twovar_line_par(c(1,2,3), "4")
ggsave("../img/20231224-region-swap-effect-1to3-on-4.png")
my_plot_region_effect_twovar_side <- function(region){
  # this function uses my_comp_region_effect to generate the data
  # and plot the difference in A_PHO2 and A_pho2 between the CgPho4 vs ScPho4
  # in the focal region
  dat <- my_comp_region_effect(region) %>% 
    pivot_longer(cols = c(dA_PHO2, dA_pho2), 
                 names_to = "host", values_to = "diff") %>% 
    mutate(host = fct_recode(host, `PHO2` = "dA_PHO2", `pho2∆` = "dA_pho2"),
           host = fct_relevel(host, "PHO2"))
  p <- dat %>% 
    ggplot(aes(x = bg, y = diff, group = host)) +
    geom_col(aes(fill = host), position = position_dodge(0.9)) +
    scale_fill_manual(values = host.colors) +
    ylab("Region swap diff (Cg vs Sc)") +
    theme_cowplot(font_size = 20) + 
    panel_border(color = "gray30") +
    background_grid(major = "y", minor = "none") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1, family = "courier"),
          axis.title.x = element_blank(),
          legend.position = "top")
  return(p)
}

triangle heatmap

First, write a function to generate the data for plotting. If we are going to use ggplot, we need a tibble to store the data, something in the following form

plasmid symbol RegionA RegionB A_PHO2 A_pho2 s_PHO2 boost perc_pho2
209 CCSCC 3 3 8.25 7.82 0.468 1.06 0.94

If we are ok with using non ggplot - heatmaps are not ggplot’s strength anyways - we can just build a matrix.

Note that this way of summarizing the data has many limitaitons: 1) it requires specifying the reference, either CCCCC or SSSSS. Everything is measured against that; 2) it only shows pairwise (two region) interactions. This turns out to be fine with five regions, since every chimera can be expressed as either a 0, 1 or 2 region swap from one of the two reference genotypes. With 6 or more regions, higher level (3 or more region) interactions cannot be visualized this way. Because of this, we will focus on just the main set for this analysis.

To build the matrix, we need to first identify the chimeras that belong to the set. For that, we will use the “main” set, with the five region split, for the moment at least. The function will first determine which reference to use. If we use SSSSS as the reference, for example, we will assign 0 to the reference. All other chimeras with 1 or 2 regions from Cg will be used to fill an upper triangular matrix, using one of the values of interest, e.g., A_PHO2.

my_upper_triangular_mat <- function(alt = "C", var = "A_PHO2"){
  # given the alternative allele (C/S) and a variable of interest, e.g., A_PHO2,
  # output an upper triangular matrix containing the values from the variable 
  # of interest, with the row and col numbers based on the first and second
  # positions containing the alternative allele. If all positions contain the 
  # reference allele, the value is subtracted from all values in the matrix
  # when just one position is the alternative allele, the value in the diagonal
  # is set. when there are more than 2 regions containing the alternative allele
  # skip.
  out_mat <- matrix(NA, nrow = 5, ncol = 5)
  ref_val <- NA
  dat <- filter(ximera, set == "M") %>% 
    mutate(S = as.character(symbol) %>% toupper())
  for(i in seq(1, nrow(dat))){
    symbol = dat[i, "S"]
    # determine which positions contain the alternative allele
    p = str_locate_all(symbol, alt)[[1]][,"start"]
    l = length(p)   # how many positions contain the alt allele
    v = dat[[var]][i] # retrieve the value of the variable
    if(l == 0)
      ref_val = v
    else if(l == 1)
      out_mat[p, p] = v
    else if(l == 2)
      out_mat[p[1], p[2]] = v
  }
  out_mat = out_mat - ref_val
  return(out_mat)
}
my_combined_triangular_mat <- function(alt = "C"){
  # given the alternative allele (C/S), output a matrix containing the values
  # for both with and without Pho2, arranged in two complementary triagular
  # matrices, with the row and col numbers based on the first and second
  # positions containing the alternative allele. If all positions contain the 
  # reference allele, the value is subtracted from all values in the matrix
  # when just one position is the alternative allele, the value in the diagonal
  # is set. when there are more than 2 regions containing the alternative allele
  # skip.
  out_mat <- matrix(NA, nrow = 6, ncol = 6)
  upper <- cbind(NA, my_upper_triangular_mat(alt, var = "A_PHO2")) %>% 
    rbind(., NA)
  lower <- rbind(NA, t(my_upper_triangular_mat(alt, var = "A_pho2"))) %>% 
    cbind(., NA)
  out_mat = ifelse(is.na(upper), lower, upper)
  return(out_mat)
}
my_plot_triangle_heatmap <- function(alt, var){
  # this function takes the output of the function above and makes a heatmap
  # using pheatmap function, then rotates it using grid graphics
  # thanks to https://bookdown.org/rdpeng/RProgDA/the-grid-package.html#grid-graphics-coordinate-systems
  # adding title based on https://davetang.github.io/muse/pheatmap.html
  
  # construct title of plot
  ref = ifelse(alt == "C", "ScPho4", "CgPho4")
  bg = ifelse(var == "A_PHO2", "with PHO2", "w/o pho2")
  my_title <- paste("Epistasis between regions on", ref, "background", bg)
  test <- my_upper_triangular_mat(alt = alt, var = var)
  paletteLength = 50
  myColors <- colorRampPalette(c("steelblue4", "gray90", "red"))(paletteLength)
  rng <- max(abs(test), na.rm = TRUE)
  myBreaks <- c(seq(-rng, 0, length.out=ceiling(paletteLength/2) + 1), 
                seq(rng/paletteLength, rng,
                    length.out=floor(paletteLength/2)))
  p <- pheatmap::pheatmap(test, color = myColors, breaks = myBreaks,
                          border_color = NA, na_col = NA, silent = TRUE,
                          cluster_cols = FALSE, cluster_rows = FALSE)
  vp <- viewport(x = 0.5, y = 0.25,
                 width = unit(4.5, "in"), height = unit(4.5, "in"), angle = 47) 
  grid.newpage()
  pushViewport(vp)
  grid.draw(p$gtable)
  popViewport()
  grid.text(label = my_title, x = 0.5, y = 0.95, gp = gpar(fontsize = 16, fontface = "bold"))
  return(p)
}
my_plot_combined_triangle_heatmap <- function(alt){
  # this function takes the output of the function my_combined_triangular_mat()
  # using pheatmap function, then rotates it using grid graphics
  # thanks to https://bookdown.org/rdpeng/RProgDA/the-grid-package.html#grid-graphics-coordinate-systems
  # adding title based on https://davetang.github.io/muse/pheatmap.html
  
  # construct title of plot
  ref = ifelse(alt == "C", "ScPho4", "CgPho4")
  my_title <- paste("Epistasis between regions on", ref, "background")
  test <- my_combined_triangular_mat(alt = alt)
  paletteLength = 50
  myColors <- colorRampPalette(c("steelblue4", "gray90", "red"))(paletteLength)
  rng <- max(abs(test), na.rm = TRUE)
  myBreaks <- c(seq(-rng, 0, length.out=ceiling(paletteLength/2) + 1), 
                seq(rng/paletteLength, rng,
                    length.out=floor(paletteLength/2)))
  p <- pheatmap::pheatmap(test, color = myColors, breaks = myBreaks,
                          border_color = NA, na_col = NA, silent = TRUE,
                          cluster_cols = FALSE, cluster_rows = FALSE)
  vp <- viewport(x = 0.5, y = 0.45,
                 width = unit(3, "in"), height = unit(2.8, "in"), angle = 47) 
  grid.newpage()
  pushViewport(vp)
  grid.draw(p$gtable)
  popViewport()
  grid.text(label = my_title, x = 0.5, y = 0.95, 
            gp = gpar(fontsize = 16, fontface = "bold"))
  grid.text(label = "With Pho2", x = 0.1, y = 0.65, just = c("left", "top"),
            gp = gpar(fontsize = 14, fontface = "bold"))
  grid.text(label = "Without pho2", x = 0.1, y = 0.25, just = c("left", "top"), 
            gp = gpar(fontsize = 14, fontface = "bold"))
  return(p)
}
p1 <- my_plot_combined_triangle_heatmap("C")
p2 <- my_plot_combined_triangle_heatmap("S")
p1 <- my_plot_triangle_heatmap("C", "A_PHO2")
p2 <- my_plot_triangle_heatmap("C", "A_pho2")
p3 <- my_plot_triangle_heatmap("S", "A_PHO2")
p4 <- my_plot_triangle_heatmap("S", "A_pho2")
LS0tCnRpdGxlOiAiRTAxMyBQaG80IGNoaW1lcmEgYWN0aXZpdHkgYW5hbHlzaXMgdXNpbmcgUEhPNSByZXBvcnRlciwgYW5hbHlzaXMiCmF1dGhvcjogIkJpbiBIZSIKZGF0ZTogIjIwMjQtMDEtMDMgdXBkYXRlZCBgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpyZXF1aXJlKHBsb3RseSkKcmVxdWlyZSh0aWR5dmVyc2UpCnJlcXVpcmUoZ2dyaWRnZXMpCnJlcXVpcmUoY293cGxvdCkKcmVxdWlyZShSQ29sb3JCcmV3ZXIpCnJlcXVpcmUoZ3JpZCkKcmVxdWlyZShnZ3RleHQpCmBgYAoKYGBge3J9Cm9sZCA8LSB0aGVtZV9zZXQodGhlbWVfYncoYmFzZV9zaXplID0gMTYpKQpgYGAKClRoaXMgaXMgdGhlIHNlY29uZCBwYXJ0IG9mIHRoZSBhbmFseXNpcy4gSW4gdGhlIGZpcnN0IHBhcnQgKHNlZSBgMjAyMzEyMjktcG9vbC1xYy1QSE81LlJtZGApLCBJIGRpZCBRQyBhbmQgZXhwb3J0ZWQgdGhlIGZpbHRlcmVkIGRhdGFzZXQuIEhlcmUsIEkgd2lsbCBjb250aW51ZSB3b3JraW5nIHdpdGggdGhhdCBkYXRhc2V0IGFuZCBhbnN3ZXIgb3VyIGJpb2xvZ2ljYWwgcXVlc3Rpb25zLiBUaGlzIG5vdGVib29rIGlzIGFuIHVwZGF0ZWQgY29weSBvZiBgMjAyNDAxMDMtUEhPNS1hbmFseXNpcy5SbWRgLiBUaGUgbWFpbiB1cGRhdGUgaXMgdG8gY29tcGFyZSB0aGUgbmV3IDQvNSBicmVhayBwb2ludCBjaGltZXJhcyB0byB0aGUgb2xkIG9uZXMuCgojIEdvYWwKLSBBbmFseXplIHRoZSBmdWxsIGNoaW1lcmEgc2V0IGZsb3cgcmVzdWx0cyBmb3IgX1BITzVwcl8tbUNoZXJyeSByZXBvcnRlci4KLSBEZXZlbG9wIGFuIGFuYWx5c2lzIHBpcGVsaW5lIHRvIHBlcmZvcm0gUUMsIGNvcnJlY3Rpb24gKGlmIG5lZWRlZCkgYW5kIHBsb3R0aW5nIHRoZSByZXN1bHRzLgotIENvbXBhcmUgdGhlIG5ldyA0LzUgYnJlYWtwb2ludCBjaGltZXJhcyB0byB0aGUgb2xkIHNldC4KCnwgU3ltYm9sIHwgT2xkIHwgTmV3IHwgQ29tbWVudCB8Cnw6LS0tLS0tLXw6LS0tLXw6LS0tLXw6LS0tLS0tLS18CnwgQ0NDQ1MgfCAyMTAgfCA0MDYgfHwKfCBDQ0NTQyB8IDIxMSB8IDQyMiB8fAp8IFNDQ0NTIHwgMjE2IHwgNDA4IHwgPTMzMiB8CnwgQ0NTU0MgfCAyMTggfCA0MjAgfHwKfCBDU0NTQyB8IDIyMyB8IDQyMyB8fAp8IFNDU0NTIHwgMjI0IHwgNDE0IHx8CnwgU1NTQ1MgfCAyMjkgfCA0MTUgfHwKfCBTU1NTQyB8IDIzMyB8IDQwNyB8fAp8IFNTQ0NTIHwgMjM1IHwgNDA5IHx8CnwgU1NDU0MgfCAyMzkgfCA0MjQgfHwKfCBDQ1NDUyB8IDI0MCB8IDQxMyB8fAp8IENTU1NDIHwgMjQxIHwgNDEwIHx8CnwgQ1NTQ1MgfCAyNTAgfCA0MTIgfHwKfCBTQ0NTQyB8IDI1MSB8IDQyNSB8fAp8IFNDU1NDIHwgMjUyIHwgNDIxIHx8CnwgQ1NDQ1MgfCAyNTMgfCA0MTEgfHwKfCB8IHwgfAp8IENTU2NzQyB8IDI1NCB8IDQxOSB8fAp8IENDQ2NzQyB8IDI1OCB8IDQxOCB8fAp8IFNTU3NjUyB8IDI3OCB8IDQxNiB8fAp8IENTU3NjUyB8IDI3OSB8IDQxNyB8fAoKIyBEYXRhCkltcG9ydCB0aGUgYmFja2dyb3VuZCBzdWJ0cmFjdGVkIGRhdGEKYGBge3J9CmRhdDAgPC0gcmVhZF90c3YoIi4uL2lucHV0LzIwMjMxMjMwLVBITzUtYmctc3VidHJhY3RlZC1kYXRhLnRzdiIsIGNvbF90eXBlcyA9ICJjY2NjY2RkZGRkYyIpCnByaW50KCJEYXRlIG9mIGV4cGVyaW1lbnRzIGFuZCAjIG9mIGZhaWxlZCBzYW1wbGVzIikKd2l0aChkYXQwLCB0YWJsZShkYXRlLCBmbGFnKSkKYGBgCgpGaWx0ZXIgdGhlIGRhdGEKYGBge3J9CmRhdCA8LSBmaWx0ZXIoZGF0MCwgaG9zdCAhPSAiUEhPODQiLCBmbGFnID09ICJwYXNzIiwgZGF0ZSAhPSAiMDIvMTAiKSAlPiUgCiAgIyBiYXNlZCBvbiBwcmV2aW91cyBRQywgdGhlIGZvbGxvd2luZyBzYW1wbGUgKGJvdGggcmVwbGljYXRlcykgaGF2ZSBoaWdoCiAgIyB2YXJpYW5jZSAtIG9uZSBiaW9sb2dpY2FsIHJlcGxpY2F0ZSBpcyBoaWdobHkgZXhwcmVzc2VkLCB3aGlsZSB0aGUgb3RoZXIgCiAgIyB0d28gaGF2ZSBtTmVvbiwgYnV0IGJhcmVseSBhbnkgUkZQIGV4cHJlc3Npb24uCiAgbXV0YXRlKGZsYWcgPSBjYXNlX3doZW4oCiAgICBwbGFzbWlkID09ICIyMzMiICYgaG9zdCA9PSAicGhvMuKIhiIgfiAiaGlnaC52YXIiLCAKICAgIHBsYXNtaWQgPT0gIjQyMyIgJiBob3N0ID09ICJQSE8yIiB+ICJoaWdoLnZhciIsIAogICAgLmRlZmF1bHQgPSBmbGFnCiAgKSkKYGBgCgpDaGltZXJhIG1ha2V1cCBpbmZvcm1hdGlvbgpgYGB7cn0KbWV0YSA8LSByZWFkX3RzdigiLi4vaW5wdXQvMjAyMzEyMjgtY2hpbWVyYS1QaG80LW1ha2V1cC50eHQiLCAKICAgICAgICAgICAgICAgICBjb21tZW50ID0gIiMiLCBjb2xfdHlwZXMgPSAiY2NjY2MiKQpgYGAKCiMjIFN1bW1hcml6ZSBkYXRhCgpIZXJlIHdlIHdvdWxkIGxpa2UgY2FsY3VsYXRlIHRoZSByYXRpbyBvZiBSRlAvR0ZQIGZvciBlYWNoIGNoaW1lcmEgKHBsYXNtaWQpIGFjcm9zcyBhbGwgcmVwbGljYXRlcywgaW5jbHVkaW5nIGZyb20gZGlmZmVyZW50IGRheXMuIE5vdGUgdGhhdCB0aGUgcGFyYW1ldGVyIG9mIGludGVyZXN0IGlzIGEgcmF0aW8sIHdoaWNoIGNhbiBiZSBlc3RpbWF0ZWQgdXNpbmcgZWl0aGVyICJtZWFucyBvZiByYXRpb3MiIG9yICJyYXRpb3Mgb2YgbWVhbnMiLiBUaGVzZSBhcmUganVzdCB0d28gc3BlY2lmaWMgaW5zdGFuY2VzIG9mIGEgbW9yZSBnZW5lcmFsIGVzdGltYXRvciwgcmVwcmVzZW50aW5nIHR3byBjaG9pY2VzIG9mIHRoZSB3ZWlnaHRzLiBUaGUgIm1lYW5zIG9mIHJhdGlvcyIgZmlyc3QgY2FsY3VsYXRlcyB0aGUgcmF0aW9zIGZvciBlYWNoIHJlcGxpY2F0ZSB3aXRoaW4gYSBwbGFzbWlkLCB0aGVuIGF2ZXJhZ2UgdGhlbS4gSW4gdGhpcyBjYWxjdWxhdGlvbiwgZWFjaCByZXBsaWNhdGUgaXMgZ2l2ZW4gdGhlIHdlaWdodCBvZiAxL24gKGVxdWFsKS4gVGhlICJyYXRpb3Mgb2YgbWVhbnMiIGZpcnN0IHN1bSB1cCB0aGUgR0ZQIGFuZCBSRlAgdmFsdWVzIHNlcGFyYXRlbHkgYWNyb3NzIHRoZSByZXBsaWNhdGVzIGZvciBlYWNoIHBsYXNtaWQsIHRoZW4gdGFrZSB0aGUgcmF0aW8gYmV0d2VlbiB0aGVtLiBJbiB0aGlzIGVzdGltYXRvciwgdGhlIHdlaWdodCBmb3IgZWFjaCByZXBsaWNhdGUgaXMgeCAvIHN1bSh4KSwgd2hlcmUgeCBpcyB0aGUgZGVub21pbmF0b3IgaW4gdGhlIHJhdGlvLCBpLmUuLCBHRlAuIEluIG90aGVyIHdvcmRzLCB0aGlzIGVzdGltYXRvciB3aWxsIGdpdmUgbW9yZSB3ZWlnaHRzIHRvIHRoZSByZXBsaWNhdGVzIHdoZXJlIHRoZSBjaGltZXJhIGhhZCBhIGhpZ2hlciBleHByZXNzaW9uIGxldmVsLgoKQm90aCBlc3RpbWF0b3JzIGFyZSBrbm93biB0byBiZSBiaWFzZWQuIFdlIHdpbGwgaWdub3JlIHRoYXQgZm9yIHRoZSBtb21lbnQuIEluIHRlcm1zIG9mIGEgY2hvaWNlIGJldHdlZW4gdGhlIHR3bywgaXQgc2VlbXMgdGhhdCB0aGVyZSBpcyBubyByZWFzb24gdG8gZ2l2ZSBtb3JlIHdlaWdodHMgdG8gdGhlIGV4cGVyaW1lbnRzIHdpdGggYSBoaWdoZXIgR0ZQIHNpZ25hbC4gU28sIHRoZSAibWVhbnMgb2YgcmF0aW9zIiBzZWVtcyBhIG1vcmUgbmF0dXJhbCBjaG9pY2UuIEhvd2V2ZXIsIHdlIHdpbGwgY2FsY3VsdGFlIGJvdGggYW5kIGRlZGljZSBsYXRlci4KCgpBIGZpbmFsIHF1ZXN0aW9uIGlzIGhvdyB0byBjYWxjdWxhdGUgdGhlIHZhcmlhbmNlIG9mIHRoZSByYXRpbyBlc3RpbWF0ZS4gQWNjb3JkaW5nIHRvIHRoZSBgc3VydmV5YCBwYWNrYWdlIFttYW51YWxdKGh0dHBzOi8vcnN0dWRpby1wdWJzLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tLzE3ODk2NV9mYjYwYTBmN2JiYjQ0YTZlYTIxOTcxM2ZiMWE4OWEyMi5odG1sKSwgYW4gYXBwcm94aW1hdGUgZXN0aW1hdG9yIGZvciB0aGUgdmFyaWFuY2UgaXMKCiQkCnIgPSBcZnJhY3tcYmFye3l9fXtcYmFye3h9fSwgXHRleHR7d2hlcmV9XCBcYmFye3l9PVxmcmFjezF9e259XHN1bV97aT0xfV57bn15X2lcIFx0ZXh0e2FuZH1cIFxiYXJ7eH09XGZyYWN7MX17bn1cc3VtX3tpPTF9XntufXhfaVwgXFwKXGhhdHtWfShyKSA9ICgxLVxmcmFje259e059KShcZnJhY3sxfXtcYmFye3h9XjJ9KVxmcmFje3Nfcl4yfXtufVwgXHRleHR7d2hlcmV9XCBzX3JeMj1cZnJhY3sxfXtuLTF9XHN1bV97aT0xfV57bn0oeV9pLXJ4X2kpXjIKJCQKCkFzc3VtaW5nIHRoYXQgTj4+biwgd2UgY2FuIGlnbm9yZSB0aGUgZmlyc3QgdGVybSBpbiB0aGUgdmFyaWFuY2UgZXN0aW1hdG9yLiBUaGUgcmVzdCBjYW4gYmUgY2FsY3VsYXRlZCBmcm9tIHRoZSBkYXRhCgpgYGB7cn0KZGF0c3VtIDwtIGRhdCAlPiUKICBmaWx0ZXIoIWlzLm5hKHBsYXNtaWQpKSAlPiUgCiAgZ3JvdXBfYnkocGxhc21pZCwgaG9zdCkgJT4lIAogIHN1bW1hcml6ZSgKICAgICBuID0gbigpLAogICAgbUcgPSBtZWFuKEJMMS5IKSwKICAgIG1SID0gbWVhbihZTDIuSCksCiAgICAgQSA9IG1lYW4oWUwyLkgvQkwxLkgpLAogICAgIHIgPSBtUi9tRywKICAgIHMyID0gMS8obi0xKSpzdW0oKFlMMi5IIC0gcipCTDEuSCleMiksCiAgICB2ciA9IDEvKG1HXjIpKnMyL24sCiAgICBzZSA9IHNxcnQodnIpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lIAogIHNlbGVjdCgtczIsIC12cikjICU+JSAKICAjcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGhvc3QsIHZhbHVlc19mcm9tID0gQkwxLkg6YG5SL0dgKSAlPiUgCiAgI211dGF0ZShgcGhvMuKIhi9QSE8yYCA9IGBSL0dfcGhvMuKIhmAvYFIvR19QSE8yYCwKICAjICAgICAgIGBuLnBobzLiiIYvUEhPMmAgPSBgblIvR19waG8y4oiGYC9gblIvR19QSE8yYCkKYGBgCgpGb3IgZWFjaCBjaGltZXJhLCB3ZSB3b3VsZCBhbHNvIGxpa2UgdG8gY2FsY3VsYXRlICoqdGhyZWUgdmFsdWVzKio6CgoxLiBBIGluIF9waG8y4oiGXzogdGhpcyBpcyBpdHMgYmFzZSBhY3Rpdml0eSB3aXRob3V0IFBobzIKMS4gQSBpbiBfUEhPMl86IHRoaXMgaXMgaXRzIGZ1bGwgYWN0aXZpdHkgd2l0aCBQaG8yCjEuIEFfUEhPMiAvIEFfcGhvMuKIhjogdGhpcyBpcyB0aGUgUGhvMiBlbmhhbmNlbWVudCBvZiBhY3Rpdml0eQoKV2UgYXNzaWduIHRoZSBjaGltZXJhcyBpbnRvIHNldmVyYWwgZ3JvdXBzLCBiYXNlZCBvbiB0aGVpciBBX1BITzIgYW5kIEFfUEhPMi9BX3BobzLiiIYKCmBgYHtyfQp4aW1lcmEgPC0gZGF0c3VtICU+JQogIG11dGF0ZShob3N0ID0gZmN0X3JlY29kZShob3N0LCAicGhvMiIgPSAicGhvMuKIhiIpKSAlPiUgCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IHBsYXNtaWQsIG5hbWVzX2Zyb20gPSBob3N0LCB2YWx1ZXNfZnJvbSA9IEEsIG5hbWVzX3ByZWZpeCA9ICJBXyIpICU+JSAKICBtdXRhdGUoCiAgICBzX1BITzIgPSBBX1BITzIgLyBBX1BITzJbcGxhc21pZCA9PSAiMTk0Il0sCiAgICBzX3BobzIgPSBBX3BobzIgLyBBX3BobzJbcGxhc21pZCA9PSAiMTk0Il0sCiAgICBib29zdCA9IEFfUEhPMiAvIEFfcGhvMiwKICAgIGdyb3VwID0gY2FzZV93aGVuKAogICAgICBwbGFzbWlkICVpbiUgYygiMTg4IiwgIjE5NCIpIH4gInJlZiIsCiAgICAgIHNfUEhPMiA8IDAuMiAgICAgICAgICAgICAgICAgfiAibi5mLiIsCiAgICAgIC5kZWZhdWx0ID0gImNoaW1lcmEiCiAgICApLAogICAgZ3JvdXAgPSBmY3RfcmVsZXZlbChncm91cCwgInJlZiIsICJjaGltZXJhIiwgIm4uZi4iKQogICkgJT4lIAogIHJpZ2h0X2pvaW4oc2VsZWN0KG1ldGEsIHBsYXNtaWQsIHNldCwgc3ltYm9sLCBmdWxsKSwgYnkgPSAicGxhc21pZCIpICU+JSAKICBtdXRhdGUoc3ltYm9sID0gZmN0X3Jlb3JkZXIoc3ltYm9sLCBzX1BITzIsIC5kZXNjID0gVFJVRSkpICU+JSAKICByZWxvY2F0ZShjKHNldCwgc3ltYm9sLCBncm91cCksIC5hZnRlciA9IHBsYXNtaWQpCmBgYAoKRGlkIHRoZSBuZXcgYnJlYWsgcG9pbnRzIHJlc2N1ZT8KYGBge3J9CnhpbWVyYSAlPiUgCiAgZmlsdGVyKHNldCAlaW4lIGMoIk4iLCAiQSIsICJTQSIsICJTTiIpKSAlPiUgCiAgYXJyYW5nZShzeW1ib2wpICU+JSAKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCB+IHJvdW5kKC54LCAyKSkpIyAlPiUgCiAgI3dyaXRlX3RzdigiLi4vb3V0cHV0LzIwMjQwMTAzLWNvbXBhcmUtb2xkLW5ldy00LTUtYnJlYWtwb2ludHMudHN2IikKYGBgCgpFeHBvcnQgdGhlIHN1bW1hcml6ZWQgZGF0YQpgYGB7cn0Kd3JpdGVfdHN2KHhpbWVyYSwgZmlsZSA9ICIuLi9vdXRwdXQvMjAyNDAxMDMtUEhPNXByLWNoaW1lcmEtc3VtbWFyaXplZC50c3YiKQpgYGAKCkZvciB0aGlzIGFuYWx5c2lzLCBpbmNsdWRlIHRoZSBzZXQgaW5mb3JtYXRpb24gaW4gc3ltYm9sCmBgYHtyfQp4aW1lcmExIDwtIG11dGF0ZSh4aW1lcmEsIHN5bWJvbCA9IHBhc3RlKHN5bWJvbCwgc2V0LCBzZXAgPSAiXyIpKQpgYGAKCiMjIERhdGEgc2VsZWN0aW9uCldyaXRlIGEgZnVuY3Rpb24gdGhhdCBjYW4gc2VsZWN0IGEgc3Vic2V0IG9mIHRoZSBjaGltZXJhcyBnaXZlbiBhIHNldCBvZiBydWxlcwpgYGB7cn0KbXlfZGF0YV9zZWxlY3QgPC0gZnVuY3Rpb24ocGF0dGVybiA9IE5VTEwsIFNldCA9IE5VTEwpewogICMgY2hhbmdlIHJlZ2lvbiA0IGludG8gYSBjb25zaXN0ZW50IGZvcm1hdAogIHRtcCA8LSB4aW1lcmEgJT4lIAogICAgbXV0YXRlKAogICAgICBzeW1ib2wgPSBhcy5jaGFyYWN0ZXIoc3ltYm9sKSwKICAgICAgU3ltYm9sID0gaWZlbHNlKAogICAgICAgIG5jaGFyKHN5bWJvbCkgPT0gNSwKICAgICAgICBwYXN0ZTAoc3RyX3N1YihzeW1ib2wsIDEsIDMpLCAKICAgICAgICAgICAgICAgc3RyX3N1YihzeW1ib2wsIDQsIDQpLCAKICAgICAgICAgICAgICAgc3RyX3N1YihzeW1ib2wsIDQsIDQpLCAKICAgICAgICAgICAgICAgc3RyX3N1YihzeW1ib2wsIDUsIDUpKSwKICAgICAgICBzeW1ib2wKICAgICAgKSkgJT4lIAogICAgc2VsZWN0KHBsYXNtaWQsIFN5bWJvbCkKICAjIHN0YXJ0aW5nIHNldAogIGlmKGxlbmd0aChTZXQpID09IDApCiAgICB4aW0gPC0gZmlsdGVyKHRtcCwgIXBsYXNtaWQgJWluJSByZWZzKQogIGVsc2UKICAgIHhpbSA8LSBmaWx0ZXIodG1wLCBzZXQgJWluJSBTZXQsICFwbGFzbWlkICVpbiUgcmVmcykKICAjIGNvbXBhcmUgdG8gdGhlIHBhdHRlcm4KICB0cnkoaWYobmNoYXIocGF0dGVybikgIT0gNikgc3RvcCgiUGF0dGVybiBtdXN0IGJlIGEgc3RyaW5nIHdpdGggNiBjaGFyYWN0ZXJzIikpCiAgc3ltYm9scyA9IHhpbSRTeW1ib2wgIyBleHRyYWMgdGhlIHN5bWJvbHMgZm9yIHRlc3RpbmcKICBpbmNsdWRlID0gbmNoYXIoc3ltYm9scykgPiAwICMgaW5pdGlhbGl6ZSB0aGUgaW5jbHVzaW9uIHZlY3RvcgogIGZvcihpIGluIDE6Nil7CiAgICBwID0gc3Vic3RyKHBhdHRlcm4sIGksIGkpCiAgICBpZihwICE9ICJYIiAmIHAgIT0gIngiKXsgIyBpZ25vcmUgWCBhbmQgeAogICAgICB0ZXN0ID0gdG91cHBlcihzdHJfc3ViKHN5bWJvbHMsIGksIGkpKSA9PSB0b3VwcGVyKHApCiAgICAgIGluY2x1ZGUgPSBpbmNsdWRlICYgdGVzdAogICAgfQogIH0KICBzZWxlY3QgPC0gY2JpbmQoeGltLCBpbmNsdWRlKQogIHJldHVybihzZWxlY3QkcGxhc21pZFtzZWxlY3QkaW5jbHVkZV0pCn0KbXlfZGF0YV9zZWxlY3RfbSA8LSBmdW5jdGlvbihwYXR0ZXJucyA9IE5VTEwsIFNldCA9IE5VTEwpewogIGFsbF9zZWxlY3RlZCA9IGMoKQogIHRyeShpZihsZW5ndGgocGF0dGVybnMpID09IDApIHN0b3AoIk5vIHBhdHRlcm5zIHByb3ZpZGVkIikpCiAgZm9yKGkgaW4gcGF0dGVybnMpCiAgICBhbGxfc2VsZWN0ZWQgPSBjKGFsbF9zZWxlY3RlZCwgbXlfZGF0YV9zZWxlY3QocGF0dGVybiA9IGkpKQogIHJldHVybih1bmlxdWUoYWxsX3NlbGVjdGVkKSkKfQpgYGAKCiMgUGxvdHRpbmcgZnVuY3Rpb25zClNldCB1cCBjb21tb24gcGFyYW1ldGVycyBmb3IgdGhyZXNob2xkaW5nIGFuZCBwbG90dGluZwpgYGB7cn0KIyByZWZlcmVuY2UgUGhvNCBwbGFzbWlkIGlkcwpyZWZzIDwtIGMoIjE4OCIsICIxOTQiKQojIGNvbG9ycwpkYXRlLmNvbG9ycyA9IGMoYnJld2VyLnBhbChuYW1lPSJEYXJrMiIsIG4gPSA4KSwgYnJld2VyLnBhbChuYW1lPSJQYWlyZWQiLCBuID0gOCkpCmhvc3QuY29sb3JzID0gYygiUEhPMiIgPSAiZ3JheTMwIiwgInBobzLiiIYiID0gImdyYXk3MCIpCnBvaW50LmNvbG9ycyA9IGMoIlBITzIiID0gImZvcmVzdGdyZWVuIiwgInBobzLiiIYiID0gInB1cnBsZTQiKQojIApgYGAKCkRhdGEgcHJlcCBhbmQgdHJhbnNmb3JtCmBgYHtyfQpteV9kYXRhX3ByZXAgPC0gZnVuY3Rpb24oc2VsZWN0aW9uKXsKICAjIGdpdmVuIGEgc2VsZWN0aW9uIG9mIGNoaW1lcmEgSUQgKHBsYXNtaWQpLCBwcmVwYXJlIGEgZGF0YSBmcmFtZSBmb3IgcGxvdHRpbmcKICAjIHN1YnNldCBkYXRhCiAgdG1wIDwtIHhpbWVyYTEgJT4lIAogICAgZmlsdGVyKHBsYXNtaWQgJWluJSBjKHJlZnMsIHNlbGVjdGlvbikpICU+JSAKICAgIHNlbGVjdChwbGFzbWlkLCBzeW1ib2wsIGdyb3VwKSAlPiUgCiAgICBpbm5lcl9qb2luKGRhdCwgYnkgPSAicGxhc21pZCIpICU+JSAKICByZXR1cm4odG1wKQp9CmBgYAoKUGxvdCBSRlAvR0ZQIHJhdGlvIGFuZCBpbmRpdmlkdWFsIGNvbXBvbmVudHMKYGBge3J9Cm15X3Bsb3RfcmF0aW8gPC0gZnVuY3Rpb24oc2VsZWN0aW9uKXsKICB0bXAgPC0gbXlfZGF0YV9wcmVwKHNlbGVjdGlvbikKICBwIDwtIHRtcCAlPiUgCiAgICBzZWxlY3QoLWMoRlNDLkgsIG5HRlAsIG5SRlAsIGZsYWcpKSAlPiUgCiAgICBtdXRhdGUoYFIvR2AgPSBZTDIuSC9CTDEuSCkgJT4lIAogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKEJMMS5ILCBZTDIuSCwgYFIvR2ApLCAKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJwYXJhbWV0ZXIiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgCiAgICBtdXRhdGUocGFyYW1ldGVyID0gZmFjdG9yKHBhcmFtZXRlciwgbGV2ZWxzID0gYygiUi9HIiwgIllMMi5IIiwgIkJMMS5IIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlJGUC9HRlAiLCAiUEhPNXBSRlAiLCAiUGhvNC1HRlAiKSkpICU+JSAKICAgIGdncGxvdChhZXMoeCA9IHN5bWJvbCwgeSA9IHZhbHVlLCBncm91cCA9IGhvc3QpKSArIAogICAgc3RhdF9zdW1tYXJ5KGFlcyhncm91cCA9IGhvc3QpLCBmdW4uZGF0YSA9ICJtZWFuX2NsX2Jvb3QiLCBnZW9tID0gImVycm9yYmFyIiwKICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNSksIHdpZHRoID0gMC4zKSArCiAgICBnZW9tX2JhcihhZXMoZmlsbCA9IGhvc3QpLCB3aWR0aCA9IDAuNSwgYWxwaGEgPSAwLjgsCiAgICAgICAgICAgICBzdGF0ID0gInN1bW1hcnkiLCBmdW4gPSAibWVhbiIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC41KSkgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZnVuY3Rpb24oeCkgc3Vic2V0KHgsICFzeW1ib2wgJWluJSBjKCJDQ0NDQyIsICJTU1NTUyIpKSwKICAgICAgICAgICAgICAgYWVzKGdyb3VwID0gaG9zdCwgY29sb3IgPSBob3N0KSwgc2l6ZSA9IDEsIHNoYXBlID0gMywgYWxwaGEgPSAwLjksCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyZG9kZ2UoZG9kZ2Uud2lkdGggPSAwLjUsIGppdHRlci53aWR0aCA9IDAuMSkpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwb2ludC5jb2xvcnMpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGhvc3QuY29sb3JzKSArCiAgICBmYWNldF9ncmlkKHBhcmFtZXRlcn5ncm91cCwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlX3giKSArCiAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAxOCkgKyBiYWNrZ3JvdW5kX2dyaWQobWlub3IgPSAibm9uZSIpICsgCiAgICB4bGFiKCJQaG80IGNoaW1lcmEiKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDMwLCBoanVzdCA9IDEsIGZhbWlseSA9ICJtb25vIiksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgcmV0dXJuKHApIAp9CmBgYAoKUGxvdCBjaGltZXJhIHJlbGF0aXZlIGFjdGl2aXR5IHRvIFNjUGhvNCB3LyBQaG8yIGFuZCBib29zdCBmYWN0b3IKYGBge3J9Cm15X3Bsb3RfcmVsX2FjdCA8LSBmdW5jdGlvbihzZWxlY3Rpb24pewogICMgZ2l2ZW4gYSBzZWxlY3Rpb24gb2YgY2hpbWVyYSBJRHMsIHBsb3QgdGhlaXIgZnVuY3Rpb25hbGl0eSB3L1BITzIKICAjIHJlbGF0aXZlIHRvIFNjUGhvNCwgYW5kIHRoZWlyIGJvb3N0CiAgcCA8LSBmaWx0ZXIoeGltZXJhLCBwbGFzbWlkICVpbiUgYyhyZWZzLCBzZWxlY3Rpb24pKSAlPiUKICAgIGdncGxvdChhZXMoeCA9IHN5bWJvbCwgeSA9IHNfUEhPMikpICsKICAgIGdlb21fY29sKHdpZHRoID0gMC4zLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiZ3JheTgwIikgKyAKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGxpbmV0eXBlID0gMiwgY29sb3IgPSAiZ3JheTMwIikgKwogICAgZmFjZXRfZ3JpZCgufmdyb3VwLCBzY2FsZXMgPSAiZnJlZV94Iiwgc3BhY2UgPSAiZnJlZV94IikgKyAKICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsKICAgIHhsYWIoIlBobzQgY2hpbWVyYSIpICsgeWxhYigiQTxzdWI+UEhPMjwvc3ViPiwgY2hpbWVyYS9TY1BobzQiKSArCiAgICBnZ3RpdGxlKCJDaGltZXJhIGZ1bmN0aW9uIGluIDxlbT5TLiBjZXJldmlzaWFlPC9lbT4iKSArCiAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAxOCkgKyBiYWNrZ3JvdW5kX2dyaWQobWlub3IgPSAibm9uZSIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIGhqdXN0ID0gMSwgZmFtaWx5ID0gIm1vbm8iKSwKICAgICAgICAgICNzdHJpcC50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X21hcmtkb3duKGhqdXN0ID0gMC41KSwKICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfbWFya2Rvd24oKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQogIHJldHVybihwKQp9CmBgYAoKYGBge3J9Cm15X3Bsb3RfYm9vc3QgPC0gZnVuY3Rpb24oc2VsZWN0aW9uKXsKICAjIGdpdmVuIGEgc2VsZWN0aW9uIG9mIGNoaW1lcmEgSURzLCBwbG90IHRoZWlyIGZ1bmN0aW9uYWxpdHkgdy9QSE8yCiAgIyByZWxhdGl2ZSB0byBTY1BobzQsIGFuZCB0aGVpciBib29zdAogICMgZGF0CiAgdG1wIDwtIGZpbHRlcih4aW1lcmEsIHBsYXNtaWQgJWluJSBjKHJlZnMsIHNlbGVjdGlvbikpICU+JSAKICAgIG11dGF0ZShwZXJjX3BobzIgPSBBX3BobzIvQV9QSE8yKSAlPiUgCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoc19QSE8yLCBib29zdCwgcGVyY19waG8yKSwgCiAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAicGFyYW1ldGVyIiwgdmFsdWVzX3RvID0gInJhdGlvIikKICAjIGxhYmVsbGVyCiAgcGFyLmV4cGxhaW4gPC0gYygKICAgIHNfUEhPMiA9ICJSZWwuIEE8c3ViPlBITzI8L3N1Yj4iLAogICAgYm9vc3QgPSAiQm9vc3QiLAogICAgcGVyY19waG8yID0gIiVBPHN1Yj5waG8y4oiGPC9zdWI+IgogICkKICBwIDwtIGdncGxvdCh0bXAsIGFlcyh4ID0gc3ltYm9sLCB5ID0gcmF0aW8pKSArCiAgICBnZW9tX2NvbCh3aWR0aCA9IDAuMywgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImdyYXk4MCIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGxpbmV0eXBlID0gMiwgY29sb3IgPSAiZ3JheTMwIikgKwogICAgZmFjZXRfZ3JpZChwYXJhbWV0ZXJ+Z3JvdXAsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZV94IiwKICAgICAgICAgICAgICBsYWJlbGxlciA9IGxhYmVsbGVyKHBhcmFtZXRlciA9IHBhci5leHBsYWluKSkgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTgpICsKICAgIGJhY2tncm91bmRfZ3JpZChtaW5vciA9ICJub25lIikgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxLCBmYW1pbHkgPSAibW9ubyIpLAogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF9tYXJrZG93bihzaXplID0gcmVsKDAuOSkpCiAgICAgICAgICAjcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkKICAgICkKICByZXR1cm4ocCkKfQpgYGAKCiMgSGlnaCB2YXJpYW5jZSBzYW1wbGVzCgpTdW1tYXJpemUgdGhlIGJhY2tncm91bmQgc3VidHJhY3RlZCBkYXRhIGJ5IGNhbGN1bGF0aW5nIHRoZSBtZWFucyBhbmQgY3YgZm9yIGVhY2ggc3RyYWluLgpgYGB7cn0KY3YgPC0gZGF0ICU+JSAKICBzZWxlY3QoLW5HRlAsIC1uUkZQKSAlPiUKICBwaXZvdF9sb25nZXIoRlNDLkg6WUwyLkgsIG5hbWVzX3RvID0gInBhcmFtZXRlciIsIHZhbHVlc190byA9ICJpbnRlbnNpdHkiKSAlPiUgCiAgZ3JvdXBfYnkoZGF0ZSwgcGxhc21pZCwgaG9zdCwgcGFyYW1ldGVyKSAlPiUgCiAgc3VtbWFyaXplKAogICAgbiA9IG4oKSwKICAgIG1lYW4gPSBtZWFuKGludGVuc2l0eSksCiAgICBjdiA9IHNkKGludGVuc2l0eSkvbWVhbihpbnRlbnNpdHkpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkgJT4lIAogIGFycmFuZ2UoZGVzYyhjdikpCmBgYAoKTnVtYmVyIG9mIHJlcGxpY2F0ZXMgbGVmdCBmb3IgZWFjaCBzYW1wbGUKYGBge3J9CmV4cHQgPC0gZGF0ICU+JSAKICBmaWx0ZXIoaG9zdCAlaW4lIGMoIlBITzIiLCAicGhvMuKIhiIpLCAhcGxhc21pZCAlaW4lIGMoIjE4OCIsICIxOTQiKSkgJT4lIAogIGdyb3VwX2J5KGRhdGUsIHBsYXNtaWQsIGhvc3QpICU+JSAKICBzdW1tYXJpemUobiA9IG4oKSwgLmdyb3VwcyA9ICJkcm9wIikKCmV4cHQgJT4lIAogIGdncGxvdChhZXMoeCA9IHBsYXNtaWQsIHkgPSBuKSkgKwogIGdlb21fY29sKGFlcyhmaWxsID0gaG9zdCkpICsgCiAgZmFjZXRfZ3JpZChkYXRlIH4gLikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlBITzIiID0gImdyYXkzMCIsICJwaG8y4oiGIiA9ICJncmF5NzAiKSkgKwogIHRoZW1lX21pbmltYWwoKSArIGJhY2tncm91bmRfZ3JpZChtYWpvciA9ICJub25lIikgKyBwYW5lbF9ib3JkZXIoc2l6ZSA9IDAuNSkgKwogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIlJlcGxpY2F0ZXMiLCBicmVha3MgPSBjKDYpKSArIHhsYWIoTlVMTCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApLAogICAgICAgIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKVXNlIHRoZSBjb250cm9sIHN0cmFpbiAocEgxOTQgd2l0aCBQSE8yKSB0byBpZGVudGlmeSBhbmQgY29ycmVjdCBmb3Igc3lzdGVtYXRpYyBiaWFzZXMKYGBge3J9CmNvbnRyb2wgPC0gZmlsdGVyKGRhdCwgcGxhc21pZCA9PSAiMTk0IiwgaG9zdCA9PSAiUEhPMiIpICU+JSAKICBzZXBhcmF0ZSh3ZWxsLCBpbnRvID0gYygicm93IiwgImNvbCIpLCBzZXAgPSAxKSAlPiUgCiAgZHJvcGxldmVscygpCmBgYAoKTW9kZWwgZm9yIG1OZW9uCmBgYHtyfQpnZnAubW9kZWwuMCA8LSBsbShCTDEuSCB+IGxvZzEwKGV2ZW50cykgKyBkYXRlICsgcm93KmNvbCwgZGF0YSA9IGNvbnRyb2wpCnN0ZXAoZ2ZwLm1vZGVsLjApCmdmcC5tb2RlbC4xIDwtIGxtKEJMMS5IIH4gZGF0ZSArIGNvbCwgZGF0YSA9IGNvbnRyb2wpCmBgYAoKTW9kZWwgZm9yIFBITzVwcjo6UkZQCmBgYHtyfQpyZnAubW9kZWwuMCA8LSBsbShZTDIuSCB+IGxvZzEwKGV2ZW50cykgKyBkYXRlICsgcm93KmNvbCwgZGF0YSA9IGNvbnRyb2wpCnN0ZXAocmZwLm1vZGVsLjApCnJmcC5tb2RlbC4xIDwtIGxtKFlMMi5IIH4gbG9nMTAoZXZlbnRzKSArIGRhdGUgKyByb3cgKyBjb2wsIGRhdGEgPSBjb250cm9sKQpgYGAKPiB0aGVyZSBhcmUgbW9yZSBzeXN0ZW1hdGljIHNoaWZ0cyBpbiB0aGUgUkZQLCBzaWduaWZpY2FudCBmb3Igcm93LCBjb2wsIGRhdGUgYW5kIGFsc28gIyBvZiBldmVudHMKPiBob3dldmVyLCBJIHdvbid0IGJlIHJlbW92aW5nIHRoZXNlIGVmZmVjdHMgeWV0LCBiZWNhdXNlIEkndmUgZm91bmQgdGhhdCBSRlAvR0ZQIHJhdGlvcyBhcmUgcHJldHR5IGNvbnNpc3RlbnQgYWNyb3NzIGRheXMuIEluIG90aGVyIHdvcmRzLCB0aGUgdmFyaWF0aW9uIGluIEdGUCBhbmQgUkZQIG1heSBiZSBjYW5jZWxsZWQgb3V0LgoKQ2hlY2sgZm9yIGVhY2ggcGxhc21pZCBob3cgY29uc2lzdGVudCBhcmUgdGhlIG1lYXN1cmVtZW50cyBiZXR3ZWVuIGRheXMKYGBge3J9CnRtcCA8LSBkYXQgJT4lIAogICMgcmVtb3ZlIG9uZSBzYW1wbGUgd2l0aCBvbmx5IG9uZSB2YWxpZCBkYXkgb2YgZXhwZXJpbWVudAogIGZpbHRlcighKHBsYXNtaWQgPT0gIjIxOCIgJiBob3N0ID09ICJQSE8yIiksICFwbGFzbWlkICVpbiUgYygiMTg4IiwgIjE5NCIsIE5BKSkgJT4lIAogIG5lc3QoZGF0YSA9IGMoZGF0ZSwgQkwxLkgsIFlMMi5IKSwgLmJ5ID0gYyhwbGFzbWlkLCBob3N0KSkKCmRheS52YXIuZ2ZwIDwtIHRtcCAlPiUgCiAgbXV0YXRlKG1vZGVsID0gbWFwKGRhdGEsIGZ1bmN0aW9uKGRmKSBsbShCTDEuSCB+IGRhdGUsIGRhdGEgPSBkZikpLAogICAgICAgICB0aWRpZWQgPSBtYXAobW9kZWwsIGJyb29tOjp0aWR5KSkgJT4lIAogIHVubmVzdCh0aWRpZWQpICU+JSAKICBmaWx0ZXIodGVybSAhPSAiKEludGVyY2VwdCkiKSAlPiUgCiAgbXV0YXRlKHAuYWRqID0gcC5hZGp1c3QocC52YWx1ZSwgbWV0aG9kID0gIkJIIikpICU+JSAKICBzZWxlY3QoLWRhdGEsIC1tb2RlbCkgJT4lIAogIGZpbHRlcihwLmFkaiA8IDAuMTApICU+JSAKICBhcnJhbmdlKHBsYXNtaWQsIGhvc3QpCgpkYXkudmFyLnJmcCA8LSB0bXAgJT4lIAogIG11dGF0ZShtb2RlbCA9IG1hcChkYXRhLCBmdW5jdGlvbihkZikgbG0oWUwyLkggfiBkYXRlLCBkYXRhID0gZGYpKSwKICAgICAgICAgdGlkaWVkID0gbWFwKG1vZGVsLCBicm9vbTo6dGlkeSkpICU+JSAKICB1bm5lc3QodGlkaWVkKSAlPiUgCiAgZmlsdGVyKHRlcm0gIT0gIihJbnRlcmNlcHQpIikgJT4lIAogIG11dGF0ZShwLmFkaiA9IHAuYWRqdXN0KHAudmFsdWUsIG1ldGhvZCA9ICJCSCIpKSAlPiUgCiAgc2VsZWN0KC1kYXRhLCAtbW9kZWwpICU+JSAKICBmaWx0ZXIocC5hZGogPCAwLjEwKSAlPiUgCiAgYXJyYW5nZShwbGFzbWlkLCBob3N0KQpgYGAKCmBgYHtyfQojIGV4dHJhY3QgeGltZXJhIG5hbWVzCnJlZnMgPC0gYygiMTg4IiwiMTk0IikKIyBtYWtlIGEgdGVzdCBzZXQKZGF5LnZhci5nZnAubGlzdCA8LSB1bmlxdWUoZGF5LnZhci5nZnAkcGxhc21pZCkKZGF5LnZhci5yZnAubGlzdCA8LSB1bmlxdWUoZGF5LnZhci5yZnAkcGxhc21pZCkKYGBgCgpIaWdoIGRheS10by1kYXkgR0ZQIHZhcmlhbmNlOiBgciBkYXkudmFyLmdmcC5saXN0YApIaWdoIGRheS10by1kYXkgUkZQIHZhcmlhbmNlOiBgciBkYXkudmFyLnJmcC5saXN0YAoKUGxvdHRpbmcgY29tcG9uZW50cyBmb3IgY2hpbWVyYXMgd2l0aCBoaWdoIGRheS10by1kYXkgdmFyaWFuY2UgaW4gUGhvNC1tTmVvbgpgYGB7cn0KcCA8LSBteV9wbG90X3JhdGlvKGMocmVmcyxkYXkudmFyLmdmcC5saXN0KSkgKyAKICAgIGdlb21fcG9pbnQoZGF0YSA9IGZ1bmN0aW9uKHgpIHN1YnNldCh4LCAhc3ltYm9sICVpbiUgYygiQ0NDQ0MiLCAiU1NTU1MiKSksCiAgICAgICAgICAgICAgIGFlcyhncm91cCA9IGhvc3QsIGNvbG9yID0gZGF0ZSksIHNpemUgPSAxLCBzaGFwZSA9IDMsIGFscGhhID0gMC45LAogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcmRvZGdlKGRvZGdlLndpZHRoID0gMC41LCBqaXR0ZXIud2lkdGggPSAwLjEpKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZGF0ZS5jb2xvcnMsIGd1aWRlID0gIm5vbmUiKQpwCmBgYAo+IFdhdGNoIG91dCBmb3IgQ1NDc2NDLCBTQ0NzUywgU0NDc1MKClBsb3R0aW5nIGNvbXBvbmVudHMgZm9yIGNoaW1lcmFzIHdpdGggaGlnaCBkYXktdG8tZGF5IHZhcmlhbmNlIGluIF9QSE81cHJfLW1DaGVycnkKYGBge3J9CnAgPC0gbXlfcGxvdF9yYXRpbyhjKHJlZnMsZGF5LnZhci5yZnAubGlzdCkpICsgCiAgICBnZW9tX3BvaW50KGRhdGEgPSBmdW5jdGlvbih4KSBzdWJzZXQoeCwgIXN5bWJvbCAlaW4lIGMoIkNDQ0NDIiwgIlNTU1NTIikpLAogICAgICAgICAgICAgICBhZXMoZ3JvdXAgPSBob3N0LCBjb2xvciA9IGRhdGUpLCBzaXplID0gMSwgc2hhcGUgPSAzLCBhbHBoYSA9IDAuOSwKICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSkpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBkYXRlLmNvbG9ycywgZ3VpZGUgPSAibm9uZSIpCnAKYGBgCj4gbW9zdCBvZiB0aGUgZGF5LXRvLWRheSB2YXJpYW5jZSBhcmUgY2FuY2VsZWQgb3V0IGFmdGVyIFJGUC9HRlAgbm9ybWFsaXphdGlvbgoKIyBBbGwgY2hpbWVyYSwgc2NhdHRlciBwbG90CkRlc2lnbiB0aGUgcGxvdAoKYGBge3J9Cm15X3NjYXR0ZXJfcGxvdCA8LSBmdW5jdGlvbihwYXR0ZXJuKXsKICBzZWxlY3Rpb24gPSBteV9kYXRhX3NlbGVjdChwYXR0ZXJuID0gcGF0dGVybikKICBzY2F0dGVyLmNvbG9ycyA9IGMoIlNjUGhvNCIgPSAiZm9yZXN0Z3JlZW4iLCAiQ2dQaG80IiA9ICJibHVlMyIsICJjeWFuMiIsICJvdGhlciIgPSAiZ3JheTIwIikKICBuYW1lcyhzY2F0dGVyLmNvbG9ycylbM10gPSBwYXR0ZXJuCiAgcCA8LSB4aW1lcmEgJT4lIAogICAgbXV0YXRlKEFfUEhPMiA9IHNpZ25pZihBX1BITzIsIGRpZ2l0cyA9IDIpLAogICAgICAgICAgIEFfcGhvMiA9IHNpZ25pZihBX3BobzIsIGRpZ2l0cyA9IDIpLAogICAgICAgICAgIGdyb3VwID0gY2FzZV93aGVuKAogICAgICAgICAgICAgc3ltYm9sID09ICJDQ0NDQyIgfiAiQ2dQaG80IiwKICAgICAgICAgICAgIHN5bWJvbCA9PSAiU1NTU1MiIH4gIlNjUGhvNCIsCiAgICAgICAgICAgICBwbGFzbWlkICVpbiUgc2VsZWN0aW9uIH4gcGF0dGVybiwKICAgICAgICAgICAgIC5kZWZhdWx0ID0gIm90aGVyIgogICAgICAgICAgICksCiAgICAgICAgICAgZ3JvdXAgPSBmY3RfcmVsZXZlbChncm91cCwgbmFtZXMoc2NhdHRlci5jb2xvcnMpKSkgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gQV9QSE8yLCB5ID0gQV9waG8yLCBsYWJlbCA9IHN5bWJvbCkpICsgCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGdyb3VwKSwgc2l6ZSA9IDIuNSkgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbCgiUGhvNCB0eXBlIiwgdmFsdWVzID0gc2NhdHRlci5jb2xvcnMpICsKICAgIGdlb21fYWJsaW5lKHNsb3BlID0gMSkgKwogICAgdGhlbWVfZ3JheShiYXNlX3NpemUgPSAxNCkKICByZXR1cm4ocCkKfQpgYGAKCnRoaXMgZnVuY3Rpb24gaXMgdGhlIHNhbWUgYXMgYWJvdmUsIGJ1dCBpcyB1c2VkIHRvIHBsb3QgcmVnaW9uIDQgZWZmZWN0cyBhbG9uZSwgYW5kIGRvZXNuJ3QgdGFrZSBhbnkgaW5wdXQKYGBge3J9Cm15X3NjYXR0ZXJfcGxvdF9maXggPC0gZnVuY3Rpb24oKXsKICAjIHRoaXMgZnVuY3Rpb24gaXMgdGhlIHNhbWUgYXMgYWJvdmUsIGJ1dCBpcyB1c2VkIHRvIHBsb3QgcmVnaW9uIDQKICAjIGVmZmVjdHMgYWxvbmUsIGFuZCBkb2Vzbid0IHRha2UgYW55IGlucHV0CiAgczEgPSBteV9kYXRhX3NlbGVjdChwYXR0ZXJuID0gIlhYWENDWCIpCiAgczIgPSBteV9kYXRhX3NlbGVjdChwYXR0ZXJuID0gIlhYWFNTWCIpCiAgc2NhdHRlci5jb2xvcnMgPSBjKCJTY1BobzQiID0gImZvcmVzdGdyZWVuIiwgIkNnUGhvNCIgPSAiYmx1ZTMiLCAKICAgICAgICAgICAgICAgICAgICAgIlAySUQ6Q2ciID0gImN5YW4yIiwgIlAySUQ6U2MiID0gInBhbGVncmVlbiIsCiAgICAgICAgICAgICAgICAgICAgICJQMklEOm1peGVkIiA9ICJncmF5MjAiKQogIHAgPC0geGltZXJhICU+JSAKICAgIG11dGF0ZShBX1BITzIgPSBzaWduaWYoQV9QSE8yLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICBBX3BobzIgPSBzaWduaWYoQV9waG8yLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICBncm91cCA9IGNhc2Vfd2hlbigKICAgICAgICAgICAgIHN5bWJvbCA9PSAiQ0NDQ0MiIH4gIkNnUGhvNCIsCiAgICAgICAgICAgICBzeW1ib2wgPT0gIlNTU1NTIiB+ICJTY1BobzQiLAogICAgICAgICAgICAgcGxhc21pZCAlaW4lIHMxIH4gIlAySUQ6Q2ciLAogICAgICAgICAgICAgcGxhc21pZCAlaW4lIHMyIH4gIlAySUQ6U2MiLAogICAgICAgICAgICAgLmRlZmF1bHQgPSAiUDJJRDptaXhlZCIKICAgICAgICAgICApLAogICAgICAgICAgIGdyb3VwID0gZmN0X3JlbGV2ZWwoZ3JvdXAsIG5hbWVzKHNjYXR0ZXIuY29sb3JzKSkpICU+JSAKICAgIGdncGxvdChhZXMoeCA9IEFfUEhPMiwgeSA9IEFfcGhvMiwgbGFiZWwgPSBzeW1ib2wpKSArIAogICAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxKSArCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGdyb3VwKSwgc2l6ZSA9IDIuNSkgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbChOVUxMLCB2YWx1ZXMgPSBzY2F0dGVyLmNvbG9ycykgKwogICAgbGFicyh4ID0gYnF1b3RlKEFbUEhPMl0pLCB5ID0gYnF1b3RlKEFbcGhvMl0pKSArCiAgICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE2KSArCiAgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkKICByZXR1cm4ocCkKfQpgYGAKCnRoaXMgZnVuY3Rpb24gaXMgdGhlIHNhbWUgYXMgbXlfc2NhdHRlcl9wbG90X2ZpeCBleGNlcHQgdGhhdCBpdCBwbG90cyBhbGwgdGhlIGNoaW1lcmFzIHdpdGhvdXQgY29sb3JpbmcgdGhlbSBkaWZmZXJlbnRseS4gZm9yIGZpZ3VyZSA1CmBgYHtyfQpteV9zY2F0dGVyX3Bsb3RfYWxsIDwtIGZ1bmN0aW9uKCl7CiAgIyB0aGlzIGZ1bmN0aW9uIGlzIHRoZSBzYW1lIGFzIG15X3NjYXR0ZXJfcGxvdF9maXggZXhjZXB0IHRoYXQgaXQgcGxvdHMgYWxsIHRoZSBjaGltZXJhcwogICMgd2l0aG91dCBjb2xvcmluZyB0aGVtIGRpZmZlcmVudGx5LiBmb3IgZmlndXJlIDUKICBzMSA9IG15X2RhdGFfc2VsZWN0KHBhdHRlcm4gPSAiWFhYQ0NYIikKICBzMiA9IG15X2RhdGFfc2VsZWN0KHBhdHRlcm4gPSAiWFhYU1NYIikKICBzY2F0dGVyLmNvbG9ycyA9IGMoIlNjUGhvNCIgPSAiZm9yZXN0Z3JlZW4iLCAiQ2dQaG80IiA9ICJibHVlMyIsIAogICAgICAgICAgICAgICAgICAgICAiUDJJRDpDZyIgPSAiZ3JheTIwIiwgIlAySUQ6U2MiID0gImdyYXkyMCIsCiAgICAgICAgICAgICAgICAgICAgICJQMklEOm1peGVkIiA9ICJncmF5MjAiKQogIHNjYXR0ZXIuc2l6ZSA9IGMoIlNjUGhvNCIgPSAzLjUsICJDZ1BobzQiID0gMy41LAogICAgICAgICAgICAgICAgICAgIlAySUQ6Q2ciID0gMi41LCAiUDJJRDpTYyIgPSAyLjUsICJQMklEOm1peGVkIiA9IDIuNSkKICBwIDwtIHhpbWVyYSAlPiUgCiAgICBtdXRhdGUoQV9QSE8yID0gc2lnbmlmKEFfUEhPMiwgZGlnaXRzID0gMiksCiAgICAgICAgICAgQV9waG8yID0gc2lnbmlmKEFfcGhvMiwgZGlnaXRzID0gMiksCiAgICAgICAgICAgZ3JvdXAgPSBjYXNlX3doZW4oCiAgICAgICAgICAgICBzeW1ib2wgPT0gIkNDQ0NDIiB+ICJDZ1BobzQiLAogICAgICAgICAgICAgc3ltYm9sID09ICJTU1NTUyIgfiAiU2NQaG80IiwKICAgICAgICAgICAgIHBsYXNtaWQgJWluJSBzMSB+ICJQMklEOkNnIiwKICAgICAgICAgICAgIHBsYXNtaWQgJWluJSBzMiB+ICJQMklEOlNjIiwKICAgICAgICAgICAgIC5kZWZhdWx0ID0gIlAySUQ6bWl4ZWQiCiAgICAgICAgICAgKSwKICAgICAgICAgICBncm91cCA9IGZjdF9yZWxldmVsKGdyb3VwLCBuYW1lcyhzY2F0dGVyLmNvbG9ycykpKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBBX1BITzIsIHkgPSBBX3BobzIsIGxhYmVsID0gc3ltYm9sKSkgKyAKICAgIGdlb21fYWJsaW5lKHNsb3BlID0gMSkgKwogICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBncm91cCwgc2l6ZSA9IGdyb3VwKSkgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbChOVUxMLCB2YWx1ZXMgPSBzY2F0dGVyLmNvbG9ycykgKwogICAgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzID0gc2NhdHRlci5zaXplLCBndWlkZSA9ICJub25lIikgKwogICAgbGFicyh4ID0gYnF1b3RlKEFbUEhPMl0pLCB5ID0gYnF1b3RlKEFbcGhvMl0pKSArCiAgICB0aGVtZV9jb3dwbG90KCkgKyBwYW5lbF9ib3JkZXIoY29sb3IgPSAiZ3JheTMwIiwgc2l6ZSA9IDEuMikgKwogICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjgpKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9IDIsIHNpemUgPSByZWwoMS4yKSksCiAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCkpCgogIHJldHVybihwKQp9CmBgYAoKYGBge3J9CnAgPC0gbXlfc2NhdHRlcl9wbG90X2ZpeCgpCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9pbWcvMjAyMzEyMjAtYWxsLWNoaW1lcmEtc2NhdHRlci1jb2xvci1ieS1QMklELnBuZyIsCiAgICAgICBwbG90ID0gcCwgd2lkdGggPSA2LCBoZWlnaHQgPSA0LCBkcGkgPSAzMDApCmdncGxvdGx5KHAgKyBsYWJzKHggPSAiQTxzdWI+UEhPMjwvc3ViPiIsIHkgPSAiQTxzdWI+cGhvMjwvc3ViPiIpICsKICAgICAgICAgICB0aGVtZV9ncmF5KGJhc2Vfc2l6ZSA9IDE2KSArCiAgICAgICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X21hcmtkb3duKCkpLCAKICAgICAgICAgdG9vbHRpcCA9IGMoImxhYmVsIiwgIngiLCAieSIpKQpgYGAKCiMgQU5PVkEKYGBge3J9CnNwbGl0IDwtIGMoMSwxLDEsMSwxKTsgbmFtZXMoc3BsaXQpIDwtIHBhc3RlMCgiUCIsIDE6NSkKdG1wIDwtIHhpbWVyYSAlPiUgCiAgZmlsdGVyKHNldCA9PSAiTSIsIGdyb3VwICE9ICJuLmYuIikgJT4lIAogIHNlcGFyYXRlX3dpZGVyX3Bvc2l0aW9uKHN5bWJvbCwgc3BsaXQpICU+JSAKICBtdXRhdGUoYWNyb3NzKFAxOlA1LCB+ZmFjdG9yKC54LCBsZXZlbHMgPSBjKCJTIiwgIkMiKSkpKQpsbSA8LSBsbShBX3BobzIgfiAoUDErUDIrUDMrUDQrUDUpLCBkYXRhID0gdG1wKQpzdW1tYXJ5KGxtKQpgYGAKVGhlIG1haW4gZWZmZWN0cyB3ZXJlIGNhbGN1bGF0ZWQgYnkgYXZlcmFnaW5nIG92ZXIgYWxsIGNoaW1lcmFzIHdpdGggQ2dQaG80IHJlZ2lvbiBhdCB0aGUgcmVzcGVjdGl2ZSBwb3NpdGlvbi4gSSdkIGxpa2UgdG8gYnJlYWsgdGhlbSBkb3duIGJ5IGJhY2tncm91bmRzLiBGb3IgZXhhbXBsZSwgZm9yIHJlZ2lvbiAzLCBJJ2QgbGlrZSB0byBzZWUgdGhlIHBhaXJ3aXNlIGNvbXBhcmlzb25zIGJldHdlZW4gQ0NDU1MgYW5kIENDU1NTLCB3aGVyZSBvbmx5IHJlZ2lvbiAzIGRpZmZlcnMuIFRoZSBzdGVwcyBhcmUKCjEuIHNlbGVjdCB0aGUgcmVnaW9uIHRvIGJlIGNvbXBhcmVkLiBzcGxpdCB0aGUgc3ltYm9sIGludG8gdHdvIHBhcnRzIC0gdGhlIGdlbm90eXBlIG9mIHRoZSBmb2NhbCByZWdpb24gYW5kIHRoZSByZXN0CjIuIGdyb3VwIGJ5IHRoZSBzZWNvbmQgcGFydCAocmVzdCkgYW5kIGNhbGN1bGF0ZSB0aGUgZGlmZmVyZW50aWFsCgpgYGB7cn0KbXlfY2FsY19yZWdpb25fZWZmZWN0IDwtIGZ1bmN0aW9uKHJlZ2lvbiwgdmFyaWFibGUpewogICMgdGhpcyBmdW5jdGlvbiB0YWtlcyB0aGUgbmFtZSBvZiBhIHZhcmlhYmxlIG9mIGludGVyZXN0CiAgIyB4IHNwZWNpZmllcyB0aGUgZm9yZWdyb3VuZCByZWdpb24sIHdoaWNoIHdpbGwgYmUgZXhhbWluZWQgZm9yIGl0cyBlZmZlY3Qgb24KICAjIHRoZSB2YXJpYWJsZSBvZiBpbnRlcmVzdC4KICAjIGl0IHRoZW4gdHJhbnNmb3JtcyB0aGUgeGltZXJhIGRhdGEgZnJhbWUgdG8gcHJlc2VydmUgb25seSB0aGUgdmFyaWFibGUgb2YKICAjIGludGVyZXN0LCBwaXZvdHMgaXQgd2lkZXIgYWZ0ZXIgZ3JvdXBpbmcgYnkgdGhlIGJhY2tncm91bmQgY29tcG9zaXRpb24uCiAgCiAgIyBwcmVwYXJlIHRoZSBkYXRhIGJ5IG11dGF0aW5nIHRoZSBzeW1ib2wgY29sdW1uIGludG8gZmcgYW5kIGJnCiAgdmFsaWQudmFyIDwtIGMoIkFfUEhPMiIsICJBX3BobzIiLCAic19QSE8yIiwgInNwaG8yIiwgImJvb3N0IikKICBpZighdmFyaWFibGUgJWluJSB2YWxpZC52YXIpCiAgICBzdG9wKHBhc3RlMCgiUGxlYXNlIHNwZWNpZnkgb25lIG9mIHRoZSB2YWxpZCB2YXJpYWJsZSBuYW1lczoiLCAKICAgICAgICAgICAgICAgIHBhc3RlKHZhbGlkLnZhciwgY29sbGFwc2UgPSAiLCAiKSkpCiAgdG1wIDwtIHhpbWVyYSAlPiUgCiAgICBmaWx0ZXIoc2V0ID09ICJNIikgJT4lIAogICAgc2VsZWN0KHBsYXNtaWQsIHN5bWJvbCwgdmFyID0ge3sgdmFyaWFibGUgfX0pICU+JSAKICAgIG11dGF0ZShmZyA9IHN0cl9zdWIoc3ltYm9sLCByZWdpb24sIHJlZ2lvbikgJT4lIHRvdXBwZXIoKSwKICAgICAgICAgICBiZyA9IHN5bWJvbCAlPiUgdG91cHBlcigpKQogICMgcmVwbGFjZSB0aGUgZm9yZWdyb3VuZCByZWdpb24gd2l0aCBYIGZvciBncm91cGluZwogIHN0cl9zdWIodG1wJGJnLCByZWdpb24sIHJlZ2lvbikgPC0gIlgiCiAgIyByZW9yZ2FuaXplIHRoZSB0aWJibGUgZm9yIGVhc2llciBoYW5kbGluZywgb3B0aW9uYWwKICB0bXAgPC0gcmVsb2NhdGUodG1wLCBmZywgYmcsIC5iZWZvcmUgPSBzeW1ib2wpICU+JSBzZWxlY3QoLXN5bWJvbCkKICAjIHBpdm90IHRoZSBkYXRhIGludG8gYSB3aWRlIGZvcm1hdCBzdWNoIHRoYXQgZm9yIGVhY2ggYmFja2dyb3VuZCwgdGhlcmUKICAjIGFyZSB0d28gdmFsdWVzIGZvciB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QsIG9uZSBmcm9tIHRoZSBjaGltZXJhIHdpdGggCiAgIyBDZ1BobzQncyB2ZXJzaW9uIGluIHRoZSBmb3JlZ3JvdW5kIGFuZCBhbm90aGVyIHdpdGggU2NQaG80J3MgdmVyc2lvbgogIHRtcCA8LSB0bXAgJT4lIAogICAgc2VsZWN0KHBsYXNtaWQsIGZnLCBiZywgdmFyKSAlPiUgCiAgICBwaXZvdF93aWRlcihpZF9jb2xzID0gYmcsIG5hbWVzX2Zyb20gPSAiZmciLCAKICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gYyhwbGFzbWlkLCB2YXIpKSAlPiUgCiAgICB1bml0ZShwbGFzbWlkLCBzdGFydHNfd2l0aCgicGxhc21pZCIpKSAlPiUKICAgIG11dGF0ZShsYWJlbCA9IHBhc3RlKGJnLCBwbGFzbWlkLCBzZXAgPSAiXG4iKSkKICByZXR1cm4odG1wKQp9CgpteV9wbG90X3JlZ2lvbl9lZmZlY3Rfb25ldmFyIDwtIGZ1bmN0aW9uKHJlZ2lvbiwgdmFyaWFibGUpewogICMgdGhpcyBmdW5jdGlvbiB1c2VzIGBteV9jYWxjX3JlZ2lvbl9lZmZlY3RgIG91dHB1dCBhcyB0aGUgZGF0YQogICMgYW5kIG1ha2VzIGEgeHkgc2NhdHRlciBwbG90LCB3aGVyZSB4IHNob3dzIHRoZSB2YWx1ZSBvZiB0aGUgdmFyaWFibGUgb2YgCiAgIyBpbnRlcmVzdCB3aXRoIENnUGhvNCBpbiB0aGUgZm9jYWwgcmVnaW9uLCBhbmQgeSBmb3IgdGhlIFNjUGhvNCB2ZXJzaW9uCiAgdG1wIDwtIG15X2NhbGNfcmVnaW9uX2VmZmVjdChyZWdpb24sIHZhcmlhYmxlKQogIHAgPC0gZ2dwbG90KHRtcCwgYWVzKHggPSB2YXJfQywgeSA9IHZhcl9TLCBsYWJlbCA9IGxhYmVsKSkgKwogICAgZ2VvbV9wb2ludChzaXplID0gMi41KSArIAogICAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxKSArCiAgICB4bGFiKHBhc3RlMCgiUmVnaW9uICIsIHJlZ2lvbiwgIiBmcm9tIENnUGhvNCIpKSArCiAgICB5bGFiKHBhc3RlMCgiUmVnaW9uICIsIHJlZ2lvbiwgIiBmcm9tIFNjUGhvNCIpKSArCiAgICB4bGltKDAsIE5BKSArIHlsaW0oMCwgTkEpICsKICAgIGdndGl0bGUocGFzdGUwKCJFZmZlY3Qgb24gIiwgdmFyaWFibGUpKSArCiAgICB0aGVtZV9ncmF5KGJhc2Vfc2l6ZSA9IDE2KSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKICByZXR1cm4ocCkKfQpgYGAKCmBgYAp4ID0gNQpwMSA8LSBteV9wbG90X3JlZ2lvbl9lZmZlY3Rfb25ldmFyKHgsICJBX1BITzIiKQpwMiA8LSBteV9wbG90X3JlZ2lvbl9lZmZlY3Rfb25ldmFyKHgsICJBX3BobzIiKQpzdWJwbG90KHAxLCBwMiwgbWFyZ2luID0gMC4wNSkgJT4lIAogIGxheW91dCh0aXRsZSA9IHBhc3RlKCJSZWdpb24iLCB4LCAic3dhcCBlZmZlY3Qgb24gQV9QSE8yIGFuZCBBX3BobzIiLCBzZXAgPSAiICIpLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSBwYXN0ZTAoIlJlZ2lvbiAiLCB4LCAiIGZyb20gQ2dQaG80IikpLAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSBwYXN0ZTAoIlJlZ2lvbiAiLCB4LCAiIGZyb20gU2NQaG80IikpICkKYGBgCgpIZXJlLCBJJ2QgbGlrZSB0byB0YWtlIHdoYXQgSSBidWlsZCBhYm92ZSBhbmQgY3JlYXRlIGEgbmV3IHRpYmJsZSwgaW4gd2hpY2ggZWFjaCByb3cgaXMgYSBkaWZmZXJlbnQgYmFja2dyb3VuZCAobWFrZXVwIG9mIHRoZSBjaGltZXJhIGV4Y2VwdCBmb3IgdGhlIGZvY2FsIHJlZ2lvbikuIFRoZSB2YWx1ZSBjb2x1bW5zIGFyZToKCjEuIGRBX1BITzIgPSBBX1BITzJfQ2cgLSBBX1BITzJfU2MKMi4gZEFfcGhvMiA9IEFfcGhvMl9DZyAtIEFfcGhvMl9TYwozLiBBX1BITzJfU2MgPSBBX1BITzJfU2MKClRoZSBnb2FsIGlzIHRvIHBsb3QgZEFfUEhPMiBhbmQgZEFfcGhvMiBzaWRlLWJ5LXNpZGUgZm9yIGVhY2ggYmFja2dyb3VuZC4KYGBge3J9Cm15X2NvbXBfcmVnaW9uX2VmZmVjdCA8LSBmdW5jdGlvbihyZWdpb24pewogICMgdGhpcyBmdW5jdGlvbiB1c2VzIG15X2NhbGNfcmVnaW9uX2VmZmVjdCB0byBnZXQgdGhlIHZhbHVlIGZvciB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QKICAjIHdpdGggZWl0aGVyIENnIG9yIFNjIHZlcnNpb24gaW4gdGhlIGZvY2FsIHJlZ2lvbiwgc2VwYXJhdGVseSBmb3IgZWFjaCBiYWNrZ3JvdW5kIGNvbXBvc2l0aW9uCiAgIyBpdCBkb2VzIHNvIGZvciB0d28gdmFyaWFibGVzLCBBX1BITzIgYW5kIEFfcGhvMiwgdGhlbiBjYWxjdWxhdGUgZEFfUEhPMiwgZEFfcGhvMiwgYW5kCiAgIyBjb21iaW5lIHRoZW0KICBQSE8yID0gbXlfY2FsY19yZWdpb25fZWZmZWN0KHJlZ2lvbiwgIkFfUEhPMiIpICU+JSAKICAgIG11dGF0ZShkQV9QSE8yID0gdmFyX0MgLSB2YXJfUywKICAgICAgICAgICAjIG1lYW4gQV9QSE8yCiAgICAgICAgICAgTV9QSE8yID0gKHZhcl9TICsgdmFyX0MpLzIsCiAgICAgICAgICAgTkYgPSBpZmVsc2UoTV9QSE8yIDw9My41LCBUUlVFLCBGQUxTRSkpICU+JSAKICAgIHNlbGVjdCgtdmFyX1MsIC12YXJfQykKICAKICBwaG8yID0gbXlfY2FsY19yZWdpb25fZWZmZWN0KHJlZ2lvbiwgIkFfcGhvMiIpICU+JSAKICAgIG11dGF0ZShkQV9waG8yID0gdmFyX0MgLSB2YXJfUywgCiAgICAgICAgICAgTV9waG8yID0gKHZhcl9TICsgdmFyX0MpLzIpICU+JSAKICAgIHNlbGVjdCgtdmFyX1MsIC12YXJfQykKICAKICBkYXQgPC0gZnVsbF9qb2luKFBITzIsIHBobzIsIGJ5ID0gYygiYmciLCAicGxhc21pZCIsICJsYWJlbCIpKSAlPiUgCiAgICBzZWxlY3QoYmcsIHBsYXNtaWQsIGRBX1BITzIsIGRBX3BobzIsIE1fUEhPMiwgTV9waG8yLCBORikKICAKICByZXR1cm4oZGF0KQp9CgpteV9wbG90X3JlZ2lvbl9lZmZlY3RfdHdvdmFyX2xpbmUgPC0gZnVuY3Rpb24ocmVnaW9uLCBoaWdobGlnaHQgPSAibm9uZSIpewogICMgdGhpcyBmdW5jdGlvbiB1c2VzIG15X2NvbXBfcmVnaW9uX2VmZmVjdCB0byBnZW5lcmF0ZSB0aGUgZGF0YQogICMgYW5kIHBsb3QgdGhlIGRpZmZlcmVuY2UgaW4gQV9QSE8yIGFuZCBBX3BobzIgYmV0d2VlbiB0aGUgQ2dQaG80IHZzIFNjUGhvNAogICMgaW4gdGhlIGZvY2FsIHJlZ2lvbgogIGRhdCA8LSBteV9jb21wX3JlZ2lvbl9lZmZlY3QocmVnaW9uKSAlPiUgCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoZEFfUEhPMiwgZEFfcGhvMiksIAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImhvc3QiLCB2YWx1ZXNfdG8gPSAiZGlmZiIpICU+JSAKICAgIG11dGF0ZShob3N0ID0gZmN0X3JlY29kZShob3N0LCBgUEhPMmAgPSAiZEFfUEhPMiIsIGBwaG8y4oiGYCA9ICJkQV9waG8yIiksCiAgICAgICAgICAgaG9zdCA9IGZjdF9yZWxldmVsKGhvc3QsICJQSE8yIikpCiAgaWYoaGlnaGxpZ2h0ICE9ICJub25lIiAmIGhpZ2hsaWdodCAhPSByZWdpb24pewogICAgaGwgPSBhcy5udW1lcmljKGhpZ2hsaWdodCkKICAgIGRhdCA8LSBtdXRhdGUoZGF0LCBncnAgPSBzdHJfc3ViKGJnLCBobCwgaGwpICU+JSB0b3VwcGVyKCksCiAgICAgICAgICAgICAgICAgIGdycCA9IGZjdF9yZWNvZGUoZ3JwLCBDZ1BobzQgPSAiQyIsIFNjUGhvNCA9ICJTIikpCiAgfWVsc2V7CiAgICBkYXQgPC0gbXV0YXRlKGRhdCwgZ3JwID0gaWZlbHNlKE5GLCAibi5mLiIsICJvdGhlcnMiKSkKICB9CiAgIyBzcGVjaWZ5IGxlZ2VuZCB0aXRsZQogIGxlZ2VuZC50aXRsZSA9ICIiCiAgaWYoaGlnaGxpZ2h0ICE9ICJub25lIiAmIGhpZ2hsaWdodCAhPSByZWdpb24pewogICAgaGwgPSBhcy5udW1lcmljKGhpZ2hsaWdodCkKICAgIGRhdCA8LSBtdXRhdGUoZGF0LCBncnAgPSBzdHJfc3ViKGJnLCBobCwgaGwpICU+JSB0b3VwcGVyKCksCiAgICAgICAgICAgICAgICAgIGdycCA9IGZjdF9yZWNvZGUoZ3JwLCBDZ1BobzQgPSAiQyIsIFNjUGhvNCA9ICJTIikpCiAgICBsZWdlbmQudGl0bGUgPSBwYXN0ZSgiUmVnaW9uIiwgaGlnaGxpZ2h0LCBzZXAgPSAiICIpCiAgfWVsc2V7CiAgICBkYXQgPC0gbXV0YXRlKGRhdCwgZ3JwID0gaWZlbHNlKE5GLCAibm8iLCAieWVzIikpCiAgICBsZWdlbmQudGl0bGUgPSAiRnVuY3Rpb25hbCIKICB9CiAgIyBzcGVjaWZ5IGFycm93IGFubm90YXRpb24KICBhcnJvdy54ID0gMC43CiAgYXJyb3cueSA9IChtYXgoZGF0JGRpZmYpIC0gbWluKGRhdCRkaWZmKSkgLyA1IAogIHAgPC0gZGF0ICU+JSAKICAgIGdncGxvdChhZXMoeCA9IGhvc3QsIHkgPSBkaWZmLCBsYWJlbCA9IGJnKSkgKwogICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBncnApLCBzaXplID0gMiwgYWxwaGEgPSAwLjgsCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKDAuMDUpKSArIAogICAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGJnKSwgbGluZXdpZHRoID0gMC4yLCBhbHBoYSA9IDAuOCkgKwogICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gYXJyb3cueCwgeGVuZCA9IGFycm93LngsIHkgPSAtYXJyb3cueSwgeWVuZCA9IGFycm93LnkpLAogICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjAzLCAibnBjIiksIGVuZHMgPSAiYm90aCIpLCAKICAgICAgICAgICAgICAgICBjb2xvciA9ICJncmF5NjAiLCBsd2QgPSAxLCBhbHBoYSA9IDAuNSkgKwogICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gYXJyb3cueCAtIDAuMDUsIHhlbmQgPSBhcnJvdy54ICsgMC4wNSwgeSA9IDAsIHllbmQgPSAwKSwKICAgICAgICAgICAgICAgICBsd2QgPSAyLCBjb2xvciA9ICJncmF5NjAiKSArCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSBhcnJvdy54IC0gMC4xLCB5ID0gNSwgbGFiZWwgPSAiQ2dQaG80KysiLCAKICAgICAgICAgICAgIGFuZ2xlID0gJzkwJywgY29sb3IgPSAiZ3JheTMwIikgKwogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gYXJyb3cueCArIDAuMSwgeSA9IC01LCBsYWJlbCA9ICJTY1BobzQrKyIsIAogICAgICAgICAgICAgYW5nbGUgPSAnMjcwJywgY29sb3IgPSAiZ3JheTMwIikgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKGxlZ2VuZC50aXRsZSwgdmFsdWVzID0gYygib3JhbmdlIiwgImdyYXkyMCIpKSArCiAgICB5bGFiKCJSZWdpb24gc3dhcCBlZmZlY3QgKENnLVNjKSIpICsKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE4KSArIAogICAgdGhlbWUoCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC45KSksCiAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC44KSksCiAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuOSkpLAogICAgKQogIHJldHVybihwKQp9CgpteV9wbG90X3JlZ2lvbl9lZmZlY3RfdHdvdmFyX2xpbmUoIjQiLCAiNSIpIyAlPiUgZ2dwbG90bHkoKQpnZ3NhdmUoIi4uL2ltZy8yMDIzMTIyMS1yZWdpb24tc3dhcC1lZmZlY3QtNC1vbi01LnBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCwgZHBpID0gMTUwKQpgYGAKCmBgYHtyfQpteV9wbG90X3JlZ2lvbl9lZmZlY3RfdHdvdmFyX2xpbmUoIjUiLCAiNCIpIyAlPiUgZ2dwbG90bHkoKQpnZ3NhdmUoIi4uL2ltZy8yMDIzMTIyNC1yZWdpb24tc3dhcC1lZmZlY3QtNS1vbi00LnBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCwgZHBpID0gMjAwKQpgYGAKCmBgYHtyfQpteV9wbG90X3JlZ2lvbl9lZmZlY3RfdHdvdmFyX2xpbmVfcGFyIDwtIGZ1bmN0aW9uKHJlZ2lvbnMsIGhpZ2hsaWdodCA9ICJub25lIil7CiAgIyB0aGlzIGZ1bmN0aW9uIHVzZXMgbXlfY29tcF9yZWdpb25fZWZmZWN0IHRvIGdlbmVyYXRlIHRoZSBkYXRhCiAgIyBhbmQgcGxvdCB0aGUgZGlmZmVyZW5jZSBpbiBBX1BITzIgYW5kIEFfcGhvMiBiZXR3ZWVuIHRoZSBDZ1BobzQgdnMgU2NQaG80CiAgIyBpbiB0aGUgZm9jYWwgcmVnaW9uCiAgZGF0IDwtIG1hcF9kZnIocmVnaW9ucywgXChyZWdpb24pIG15X2NvbXBfcmVnaW9uX2VmZmVjdChyZWdpb24pLCAuaWQgPSAicmVnaW9uIikgJT4lIAogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKGRBX1BITzIsIGRBX3BobzIpLCAKICAgICAgICAgICAgICAgICBuYW1lc190byA9ICJob3N0IiwgdmFsdWVzX3RvID0gImRpZmYiKSAlPiUgCiAgICBtdXRhdGUoaG9zdCA9IGZjdF9yZWNvZGUoaG9zdCwgYFBITzJgID0gImRBX1BITzIiLCBgcGhvMuKIhmAgPSAiZEFfcGhvMiIpLAogICAgICAgICAgIGhvc3QgPSBmY3RfcmVsZXZlbChob3N0LCAiUEhPMiIpKQogICMgc3BlY2lmeSBsZWdlbmQgdGl0bGUKICBsZWdlbmQudGl0bGUgPSAiIgogIGlmKGhpZ2hsaWdodCAhPSAibm9uZSIgJiAhaGlnaGxpZ2h0ICVpbiUgcmVnaW9ucyl7CiAgICBobCA9IGFzLm51bWVyaWMoaGlnaGxpZ2h0KQogICAgZGF0IDwtIG11dGF0ZShkYXQsIGdycCA9IHN0cl9zdWIoYmcsIGhsLCBobCkgJT4lIHRvdXBwZXIoKSwKICAgICAgICAgICAgICAgICAgZ3JwID0gZmN0X3JlY29kZShncnAsIENnUGhvNCA9ICJDIiwgU2NQaG80ID0gIlMiKSkKICAgIGxlZ2VuZC50aXRsZSA9IHBhc3RlKCJSZWdpb24iLCBoaWdobGlnaHQsICJmcm9tIiwgc2VwID0gIiAiKQogIH1lbHNlewogICAgZGF0IDwtIG11dGF0ZShkYXQsIGdycCA9IGlmZWxzZShORiwgIm5vIiwgInllcyIpKQogICAgbGVnZW5kLnRpdGxlID0gIkZ1bmN0aW9uYWwiCiAgfQogICMgc3BlY2lmeSBhcnJvdyBhbm5vdGF0aW9uCiAgYXJyb3cueCA9IDAuNwogIGFycm93LnkgPSAobWF4KGRhdCRkaWZmKSAtIG1pbihkYXQkZGlmZikpIC8gNSAKICBwIDwtIGRhdCAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBob3N0LCB5ID0gZGlmZiwgbGFiZWwgPSBiZykpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gZ3JwKSwgc2l6ZSA9IDIsIGFscGhhID0gMC44LAogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcigwLjEpKSArIAogICAgZ2VvbV9saW5lKGFlcyhncm91cCA9IGJnKSwgbGluZXdpZHRoID0gMC4yLCBhbHBoYSA9IDAuOCkgKwogICAgZmFjZXRfZ3JpZChncnAgfiByZWdpb24sIGxhYmVsbGVyID0gbGFiZWxsZXIoCiAgICAgIGdycCA9IGMoQ2dQaG80ID0gIlAySUQ6Q2dQaG80IiwgU2NQaG80ID0gIlAySUQ6U2NQaG80IiksCiAgICAgIHJlZ2lvbiA9IGxhYmVsX2JvdGgKICAgICkpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCgiUDJJRDoiLCB2YWx1ZXMgPSBjKCJvcmFuZ2UiLCAiZ3JheTIwIikpICsKICAgIHlsYWIoIlJlZ2lvbiBzd2FwIGVmZmVjdCAoQ2ctU2MpIikgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTgpICsgCiAgICB0aGVtZSgKICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjkpKSwKICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjgpKSwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC45KSksCiAgICApCiAgcmV0dXJuKHApCn0KbXlfcGxvdF9yZWdpb25fZWZmZWN0X3R3b3Zhcl9saW5lX3BhcihjKDEsMiwzKSwgIjQiKQpnZ3NhdmUoIi4uL2ltZy8yMDIzMTIyNC1yZWdpb24tc3dhcC1lZmZlY3QtMXRvMy1vbi00LnBuZyIpCmBgYAoKYGBge3J9Cm15X3Bsb3RfcmVnaW9uX2VmZmVjdF90d292YXJfc2lkZSA8LSBmdW5jdGlvbihyZWdpb24pewogICMgdGhpcyBmdW5jdGlvbiB1c2VzIG15X2NvbXBfcmVnaW9uX2VmZmVjdCB0byBnZW5lcmF0ZSB0aGUgZGF0YQogICMgYW5kIHBsb3QgdGhlIGRpZmZlcmVuY2UgaW4gQV9QSE8yIGFuZCBBX3BobzIgYmV0d2VlbiB0aGUgQ2dQaG80IHZzIFNjUGhvNAogICMgaW4gdGhlIGZvY2FsIHJlZ2lvbgogIGRhdCA8LSBteV9jb21wX3JlZ2lvbl9lZmZlY3QocmVnaW9uKSAlPiUgCiAgICBwaXZvdF9sb25nZXIoY29scyA9IGMoZEFfUEhPMiwgZEFfcGhvMiksIAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gImhvc3QiLCB2YWx1ZXNfdG8gPSAiZGlmZiIpICU+JSAKICAgIG11dGF0ZShob3N0ID0gZmN0X3JlY29kZShob3N0LCBgUEhPMmAgPSAiZEFfUEhPMiIsIGBwaG8y4oiGYCA9ICJkQV9waG8yIiksCiAgICAgICAgICAgaG9zdCA9IGZjdF9yZWxldmVsKGhvc3QsICJQSE8yIikpCiAgcCA8LSBkYXQgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gYmcsIHkgPSBkaWZmLCBncm91cCA9IGhvc3QpKSArCiAgICBnZW9tX2NvbChhZXMoZmlsbCA9IGhvc3QpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSkpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGhvc3QuY29sb3JzKSArCiAgICB5bGFiKCJSZWdpb24gc3dhcCBkaWZmIChDZyB2cyBTYykiKSArCiAgICB0aGVtZV9jb3dwbG90KGZvbnRfc2l6ZSA9IDIwKSArIAogICAgcGFuZWxfYm9yZGVyKGNvbG9yID0gImdyYXkzMCIpICsKICAgIGJhY2tncm91bmRfZ3JpZChtYWpvciA9ICJ5IiwgbWlub3IgPSAibm9uZSIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgZmFtaWx5ID0gImNvdXJpZXIiKSwKICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQogIHJldHVybihwKQp9CmBgYAoKIyB0cmlhbmdsZSBoZWF0bWFwCkZpcnN0LCB3cml0ZSBhIGZ1bmN0aW9uIHRvIGdlbmVyYXRlIHRoZSBkYXRhIGZvciBwbG90dGluZy4gSWYgd2UgYXJlIGdvaW5nIHRvIHVzZSBnZ3Bsb3QsIHdlIG5lZWQgYSB0aWJibGUgdG8gc3RvcmUgdGhlIGRhdGEsIHNvbWV0aGluZyBpbiB0aGUgZm9sbG93aW5nIGZvcm0KCnwgcGxhc21pZCB8IHN5bWJvbCB8IFJlZ2lvbkEgfCBSZWdpb25CIHwgQV9QSE8yIHwgQV9waG8yIHwgc19QSE8yIHwgYm9vc3QgfCBwZXJjX3BobzIgfAp8Oi0tLS0tLS0gfDotLS0tLS0gfDotLS0tLS0tIHw6LS0tLS0tLSB8Oi0tLS0tLSB8Oi0tLS0tLSB8Oi0tLS0tLSB8Oi0tLS0tIHw6LS0tLS0tLS0tIHwKfCAyMDkgICAgIHwgQ0NTQ0MgIHwgMyAgICAgICB8IDMgICAgICAgfCA4LjI1ICAgfCA3LjgyICAgfCAwLjQ2OCAgfCAxLjA2ICB8IDAuOTQgICAgICB8CgpJZiB3ZSBhcmUgb2sgd2l0aCB1c2luZyBub24gZ2dwbG90IC0gaGVhdG1hcHMgYXJlIG5vdCBnZ3Bsb3QncyBzdHJlbmd0aCBhbnl3YXlzIC0gd2UgY2FuIGp1c3QgYnVpbGQgYSBtYXRyaXguCgpOb3RlIHRoYXQgdGhpcyB3YXkgb2Ygc3VtbWFyaXppbmcgdGhlIGRhdGEgaGFzIG1hbnkgbGltaXRhaXRvbnM6IDEpIGl0IHJlcXVpcmVzIHNwZWNpZnlpbmcgdGhlIHJlZmVyZW5jZSwgZWl0aGVyIENDQ0NDIG9yIFNTU1NTLiBFdmVyeXRoaW5nIGlzIG1lYXN1cmVkIGFnYWluc3QgdGhhdDsgMikgaXQgb25seSBzaG93cyBwYWlyd2lzZSAodHdvIHJlZ2lvbikgaW50ZXJhY3Rpb25zLiBUaGlzIHR1cm5zIG91dCB0byBiZSBmaW5lIHdpdGggZml2ZSByZWdpb25zLCBzaW5jZSBldmVyeSBjaGltZXJhIGNhbiBiZSBleHByZXNzZWQgYXMgZWl0aGVyIGEgMCwgMSBvciAyIHJlZ2lvbiBzd2FwIGZyb20gb25lIG9mIHRoZSB0d28gcmVmZXJlbmNlIGdlbm90eXBlcy4gV2l0aCA2IG9yIG1vcmUgcmVnaW9ucywgaGlnaGVyIGxldmVsICgzIG9yIG1vcmUgcmVnaW9uKSBpbnRlcmFjdGlvbnMgY2Fubm90IGJlIHZpc3VhbGl6ZWQgdGhpcyB3YXkuIEJlY2F1c2Ugb2YgdGhpcywgd2Ugd2lsbCBmb2N1cyBvbiBqdXN0IHRoZSBtYWluIHNldCBmb3IgdGhpcyBhbmFseXNpcy4KClRvIGJ1aWxkIHRoZSBtYXRyaXgsIHdlIG5lZWQgdG8gZmlyc3QgaWRlbnRpZnkgdGhlIGNoaW1lcmFzIHRoYXQgYmVsb25nIHRvIHRoZSBzZXQuIEZvciB0aGF0LCB3ZSB3aWxsIHVzZSB0aGUgIm1haW4iIHNldCwgd2l0aCB0aGUgZml2ZSByZWdpb24gc3BsaXQsIGZvciB0aGUgbW9tZW50IGF0IGxlYXN0LiBUaGUgZnVuY3Rpb24gd2lsbCBmaXJzdCBkZXRlcm1pbmUgd2hpY2ggcmVmZXJlbmNlIHRvIHVzZS4gSWYgd2UgdXNlIFNTU1NTIGFzIHRoZSByZWZlcmVuY2UsIGZvciBleGFtcGxlLCB3ZSB3aWxsIGFzc2lnbiAwIHRvIHRoZSByZWZlcmVuY2UuIEFsbCBvdGhlciBjaGltZXJhcyB3aXRoIDEgb3IgMiByZWdpb25zIGZyb20gQ2cgd2lsbCBiZSB1c2VkIHRvIGZpbGwgYW4gdXBwZXIgdHJpYW5ndWxhciBtYXRyaXgsIHVzaW5nIG9uZSBvZiB0aGUgdmFsdWVzIG9mIGludGVyZXN0LCBlLmcuLCBBX1BITzIuCmBgYHtyfQpteV91cHBlcl90cmlhbmd1bGFyX21hdCA8LSBmdW5jdGlvbihhbHQgPSAiQyIsIHZhciA9ICJBX1BITzIiKXsKICAjIGdpdmVuIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUgKEMvUykgYW5kIGEgdmFyaWFibGUgb2YgaW50ZXJlc3QsIGUuZy4sIEFfUEhPMiwKICAjIG91dHB1dCBhbiB1cHBlciB0cmlhbmd1bGFyIG1hdHJpeCBjb250YWluaW5nIHRoZSB2YWx1ZXMgZnJvbSB0aGUgdmFyaWFibGUgCiAgIyBvZiBpbnRlcmVzdCwgd2l0aCB0aGUgcm93IGFuZCBjb2wgbnVtYmVycyBiYXNlZCBvbiB0aGUgZmlyc3QgYW5kIHNlY29uZAogICMgcG9zaXRpb25zIGNvbnRhaW5pbmcgdGhlIGFsdGVybmF0aXZlIGFsbGVsZS4gSWYgYWxsIHBvc2l0aW9ucyBjb250YWluIHRoZSAKICAjIHJlZmVyZW5jZSBhbGxlbGUsIHRoZSB2YWx1ZSBpcyBzdWJ0cmFjdGVkIGZyb20gYWxsIHZhbHVlcyBpbiB0aGUgbWF0cml4CiAgIyB3aGVuIGp1c3Qgb25lIHBvc2l0aW9uIGlzIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUsIHRoZSB2YWx1ZSBpbiB0aGUgZGlhZ29uYWwKICAjIGlzIHNldC4gd2hlbiB0aGVyZSBhcmUgbW9yZSB0aGFuIDIgcmVnaW9ucyBjb250YWluaW5nIHRoZSBhbHRlcm5hdGl2ZSBhbGxlbGUKICAjIHNraXAuCiAgb3V0X21hdCA8LSBtYXRyaXgoTkEsIG5yb3cgPSA1LCBuY29sID0gNSkKICByZWZfdmFsIDwtIE5BCiAgZGF0IDwtIGZpbHRlcih4aW1lcmEsIHNldCA9PSAiTSIpICU+JSAKICAgIG11dGF0ZShTID0gYXMuY2hhcmFjdGVyKHN5bWJvbCkgJT4lIHRvdXBwZXIoKSkKICBmb3IoaSBpbiBzZXEoMSwgbnJvdyhkYXQpKSl7CiAgICBzeW1ib2wgPSBkYXRbaSwgIlMiXQogICAgIyBkZXRlcm1pbmUgd2hpY2ggcG9zaXRpb25zIGNvbnRhaW4gdGhlIGFsdGVybmF0aXZlIGFsbGVsZQogICAgcCA9IHN0cl9sb2NhdGVfYWxsKHN5bWJvbCwgYWx0KVtbMV1dWywic3RhcnQiXQogICAgbCA9IGxlbmd0aChwKSAgICMgaG93IG1hbnkgcG9zaXRpb25zIGNvbnRhaW4gdGhlIGFsdCBhbGxlbGUKICAgIHYgPSBkYXRbW3Zhcl1dW2ldICMgcmV0cmlldmUgdGhlIHZhbHVlIG9mIHRoZSB2YXJpYWJsZQogICAgaWYobCA9PSAwKQogICAgICByZWZfdmFsID0gdgogICAgZWxzZSBpZihsID09IDEpCiAgICAgIG91dF9tYXRbcCwgcF0gPSB2CiAgICBlbHNlIGlmKGwgPT0gMikKICAgICAgb3V0X21hdFtwWzFdLCBwWzJdXSA9IHYKICB9CiAgb3V0X21hdCA9IG91dF9tYXQgLSByZWZfdmFsCiAgcmV0dXJuKG91dF9tYXQpCn0KYGBgCmBgYHtyfQpteV9jb21iaW5lZF90cmlhbmd1bGFyX21hdCA8LSBmdW5jdGlvbihhbHQgPSAiQyIpewogICMgZ2l2ZW4gdGhlIGFsdGVybmF0aXZlIGFsbGVsZSAoQy9TKSwgb3V0cHV0IGEgbWF0cml4IGNvbnRhaW5pbmcgdGhlIHZhbHVlcwogICMgZm9yIGJvdGggd2l0aCBhbmQgd2l0aG91dCBQaG8yLCBhcnJhbmdlZCBpbiB0d28gY29tcGxlbWVudGFyeSB0cmlhZ3VsYXIKICAjIG1hdHJpY2VzLCB3aXRoIHRoZSByb3cgYW5kIGNvbCBudW1iZXJzIGJhc2VkIG9uIHRoZSBmaXJzdCBhbmQgc2Vjb25kCiAgIyBwb3NpdGlvbnMgY29udGFpbmluZyB0aGUgYWx0ZXJuYXRpdmUgYWxsZWxlLiBJZiBhbGwgcG9zaXRpb25zIGNvbnRhaW4gdGhlIAogICMgcmVmZXJlbmNlIGFsbGVsZSwgdGhlIHZhbHVlIGlzIHN1YnRyYWN0ZWQgZnJvbSBhbGwgdmFsdWVzIGluIHRoZSBtYXRyaXgKICAjIHdoZW4ganVzdCBvbmUgcG9zaXRpb24gaXMgdGhlIGFsdGVybmF0aXZlIGFsbGVsZSwgdGhlIHZhbHVlIGluIHRoZSBkaWFnb25hbAogICMgaXMgc2V0LiB3aGVuIHRoZXJlIGFyZSBtb3JlIHRoYW4gMiByZWdpb25zIGNvbnRhaW5pbmcgdGhlIGFsdGVybmF0aXZlIGFsbGVsZQogICMgc2tpcC4KICBvdXRfbWF0IDwtIG1hdHJpeChOQSwgbnJvdyA9IDYsIG5jb2wgPSA2KQogIHVwcGVyIDwtIGNiaW5kKE5BLCBteV91cHBlcl90cmlhbmd1bGFyX21hdChhbHQsIHZhciA9ICJBX1BITzIiKSkgJT4lIAogICAgcmJpbmQoLiwgTkEpCiAgbG93ZXIgPC0gcmJpbmQoTkEsIHQobXlfdXBwZXJfdHJpYW5ndWxhcl9tYXQoYWx0LCB2YXIgPSAiQV9waG8yIikpKSAlPiUgCiAgICBjYmluZCguLCBOQSkKICBvdXRfbWF0ID0gaWZlbHNlKGlzLm5hKHVwcGVyKSwgbG93ZXIsIHVwcGVyKQogIHJldHVybihvdXRfbWF0KQp9CmBgYAoKYGBge3J9Cm15X3Bsb3RfdHJpYW5nbGVfaGVhdG1hcCA8LSBmdW5jdGlvbihhbHQsIHZhcil7CiAgIyB0aGlzIGZ1bmN0aW9uIHRha2VzIHRoZSBvdXRwdXQgb2YgdGhlIGZ1bmN0aW9uIGFib3ZlIGFuZCBtYWtlcyBhIGhlYXRtYXAKICAjIHVzaW5nIHBoZWF0bWFwIGZ1bmN0aW9uLCB0aGVuIHJvdGF0ZXMgaXQgdXNpbmcgZ3JpZCBncmFwaGljcwogICMgdGhhbmtzIHRvIGh0dHBzOi8vYm9va2Rvd24ub3JnL3JkcGVuZy9SUHJvZ0RBL3RoZS1ncmlkLXBhY2thZ2UuaHRtbCNncmlkLWdyYXBoaWNzLWNvb3JkaW5hdGUtc3lzdGVtcwogICMgYWRkaW5nIHRpdGxlIGJhc2VkIG9uIGh0dHBzOi8vZGF2ZXRhbmcuZ2l0aHViLmlvL211c2UvcGhlYXRtYXAuaHRtbAogIAogICMgY29uc3RydWN0IHRpdGxlIG9mIHBsb3QKICByZWYgPSBpZmVsc2UoYWx0ID09ICJDIiwgIlNjUGhvNCIsICJDZ1BobzQiKQogIGJnID0gaWZlbHNlKHZhciA9PSAiQV9QSE8yIiwgIndpdGggUEhPMiIsICJ3L28gcGhvMiIpCiAgbXlfdGl0bGUgPC0gcGFzdGUoIkVwaXN0YXNpcyBiZXR3ZWVuIHJlZ2lvbnMgb24iLCByZWYsICJiYWNrZ3JvdW5kIiwgYmcpCiAgdGVzdCA8LSBteV91cHBlcl90cmlhbmd1bGFyX21hdChhbHQgPSBhbHQsIHZhciA9IHZhcikKICBwYWxldHRlTGVuZ3RoID0gNTAKICBteUNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGMoInN0ZWVsYmx1ZTQiLCAiZ3JheTkwIiwgInJlZCIpKShwYWxldHRlTGVuZ3RoKQogIHJuZyA8LSBtYXgoYWJzKHRlc3QpLCBuYS5ybSA9IFRSVUUpCiAgbXlCcmVha3MgPC0gYyhzZXEoLXJuZywgMCwgbGVuZ3RoLm91dD1jZWlsaW5nKHBhbGV0dGVMZW5ndGgvMikgKyAxKSwgCiAgICAgICAgICAgICAgICBzZXEocm5nL3BhbGV0dGVMZW5ndGgsIHJuZywKICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0PWZsb29yKHBhbGV0dGVMZW5ndGgvMikpKQogIHAgPC0gcGhlYXRtYXA6OnBoZWF0bWFwKHRlc3QsIGNvbG9yID0gbXlDb2xvcnMsIGJyZWFrcyA9IG15QnJlYWtzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLCBuYV9jb2wgPSBOQSwgc2lsZW50ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UpCiAgdnAgPC0gdmlld3BvcnQoeCA9IDAuNSwgeSA9IDAuMjUsCiAgICAgICAgICAgICAgICAgd2lkdGggPSB1bml0KDQuNSwgImluIiksIGhlaWdodCA9IHVuaXQoNC41LCAiaW4iKSwgYW5nbGUgPSA0NykgCiAgZ3JpZC5uZXdwYWdlKCkKICBwdXNoVmlld3BvcnQodnApCiAgZ3JpZC5kcmF3KHAkZ3RhYmxlKQogIHBvcFZpZXdwb3J0KCkKICBncmlkLnRleHQobGFiZWwgPSBteV90aXRsZSwgeCA9IDAuNSwgeSA9IDAuOTUsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE2LCBmb250ZmFjZSA9ICJib2xkIikpCiAgcmV0dXJuKHApCn0KYGBgCmBgYHtyfQpteV9wbG90X2NvbWJpbmVkX3RyaWFuZ2xlX2hlYXRtYXAgPC0gZnVuY3Rpb24oYWx0KXsKICAjIHRoaXMgZnVuY3Rpb24gdGFrZXMgdGhlIG91dHB1dCBvZiB0aGUgZnVuY3Rpb24gbXlfY29tYmluZWRfdHJpYW5ndWxhcl9tYXQoKQogICMgdXNpbmcgcGhlYXRtYXAgZnVuY3Rpb24sIHRoZW4gcm90YXRlcyBpdCB1c2luZyBncmlkIGdyYXBoaWNzCiAgIyB0aGFua3MgdG8gaHR0cHM6Ly9ib29rZG93bi5vcmcvcmRwZW5nL1JQcm9nREEvdGhlLWdyaWQtcGFja2FnZS5odG1sI2dyaWQtZ3JhcGhpY3MtY29vcmRpbmF0ZS1zeXN0ZW1zCiAgIyBhZGRpbmcgdGl0bGUgYmFzZWQgb24gaHR0cHM6Ly9kYXZldGFuZy5naXRodWIuaW8vbXVzZS9waGVhdG1hcC5odG1sCiAgCiAgIyBjb25zdHJ1Y3QgdGl0bGUgb2YgcGxvdAogIHJlZiA9IGlmZWxzZShhbHQgPT0gIkMiLCAiU2NQaG80IiwgIkNnUGhvNCIpCiAgbXlfdGl0bGUgPC0gcGFzdGUoIkVwaXN0YXNpcyBiZXR3ZWVuIHJlZ2lvbnMgb24iLCByZWYsICJiYWNrZ3JvdW5kIikKICB0ZXN0IDwtIG15X2NvbWJpbmVkX3RyaWFuZ3VsYXJfbWF0KGFsdCA9IGFsdCkKICBwYWxldHRlTGVuZ3RoID0gNTAKICBteUNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGMoInN0ZWVsYmx1ZTQiLCAiZ3JheTkwIiwgInJlZCIpKShwYWxldHRlTGVuZ3RoKQogIHJuZyA8LSBtYXgoYWJzKHRlc3QpLCBuYS5ybSA9IFRSVUUpCiAgbXlCcmVha3MgPC0gYyhzZXEoLXJuZywgMCwgbGVuZ3RoLm91dD1jZWlsaW5nKHBhbGV0dGVMZW5ndGgvMikgKyAxKSwgCiAgICAgICAgICAgICAgICBzZXEocm5nL3BhbGV0dGVMZW5ndGgsIHJuZywKICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0PWZsb29yKHBhbGV0dGVMZW5ndGgvMikpKQogIHAgPC0gcGhlYXRtYXA6OnBoZWF0bWFwKHRlc3QsIGNvbG9yID0gbXlDb2xvcnMsIGJyZWFrcyA9IG15QnJlYWtzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLCBuYV9jb2wgPSBOQSwgc2lsZW50ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UpCiAgdnAgPC0gdmlld3BvcnQoeCA9IDAuNSwgeSA9IDAuNDUsCiAgICAgICAgICAgICAgICAgd2lkdGggPSB1bml0KDMsICJpbiIpLCBoZWlnaHQgPSB1bml0KDIuOCwgImluIiksIGFuZ2xlID0gNDcpIAogIGdyaWQubmV3cGFnZSgpCiAgcHVzaFZpZXdwb3J0KHZwKQogIGdyaWQuZHJhdyhwJGd0YWJsZSkKICBwb3BWaWV3cG9ydCgpCiAgZ3JpZC50ZXh0KGxhYmVsID0gbXlfdGl0bGUsIHggPSAwLjUsIHkgPSAwLjk1LCAKICAgICAgICAgICAgZ3AgPSBncGFyKGZvbnRzaXplID0gMTYsIGZvbnRmYWNlID0gImJvbGQiKSkKICBncmlkLnRleHQobGFiZWwgPSAiV2l0aCBQaG8yIiwgeCA9IDAuMSwgeSA9IDAuNjUsIGp1c3QgPSBjKCJsZWZ0IiwgInRvcCIpLAogICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAxNCwgZm9udGZhY2UgPSAiYm9sZCIpKQogIGdyaWQudGV4dChsYWJlbCA9ICJXaXRob3V0IHBobzIiLCB4ID0gMC4xLCB5ID0gMC4yNSwganVzdCA9IGMoImxlZnQiLCAidG9wIiksIAogICAgICAgICAgICBncCA9IGdwYXIoZm9udHNpemUgPSAxNCwgZm9udGZhY2UgPSAiYm9sZCIpKQogIHJldHVybihwKQp9CmBgYAoKYGBge3J9CnAxIDwtIG15X3Bsb3RfY29tYmluZWRfdHJpYW5nbGVfaGVhdG1hcCgiQyIpCnAyIDwtIG15X3Bsb3RfY29tYmluZWRfdHJpYW5nbGVfaGVhdG1hcCgiUyIpCmBgYAoKYGBge3J9CnAxIDwtIG15X3Bsb3RfdHJpYW5nbGVfaGVhdG1hcCgiQyIsICJBX1BITzIiKQpwMiA8LSBteV9wbG90X3RyaWFuZ2xlX2hlYXRtYXAoIkMiLCAiQV9waG8yIikKcDMgPC0gbXlfcGxvdF90cmlhbmdsZV9oZWF0bWFwKCJTIiwgIkFfUEhPMiIpCnA0IDwtIG15X3Bsb3RfdHJpYW5nbGVfaGVhdG1hcCgiUyIsICJBX3BobzIiKQpgYGAKCg==